├── .editorconfig
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug.md
│ └── user-story.md
├── PULL_REQUEST_TEMPLATE
│ └── pull_request_template.md
└── workflows
│ ├── conventional-commits.yaml
│ ├── devcontainer-release.yaml
│ ├── devcontainer-test.yaml
│ ├── docs.yaml
│ ├── main.yaml
│ ├── pull-requests.yaml
│ └── release-please.yaml
├── .gitignore
├── .release-please-manifest.json
├── Aspirate.sln
├── CHANGELOG.md
├── Directory.Build.props
├── Directory.Build.targets
├── Directory.Packages.props
├── LICENSE.TXT
├── README.md
├── build.cake
├── docs
└── Writerside
│ ├── c.list
│ ├── hi.tree
│ ├── redirection-rules.xml
│ ├── topics
│ ├── Apply-Manifests.md
│ ├── Applying-Secrets.md
│ ├── Build-Command.md
│ ├── Commands.md
│ ├── Disable-Secret-Management.md
│ ├── General-FAQ.md
│ ├── Generate-Command.md
│ ├── Generating-Secrets.md
│ ├── Getting-Started.md
│ ├── Init-Command.md
│ ├── Installation-as-a-DevContainer-Feature.md
│ ├── Installing-as-a-Global-Tool.md
│ ├── Managing-Secrets.md
│ ├── Non-Interactive-Invocation.md
│ ├── Removing-Aspirate.md
│ ├── Removing-Manifests-from-a-Cluster.md
│ ├── Run-Command.md
│ ├── Secret-Management.md
│ └── Secrets-File-Contents.md
│ ├── v.list
│ └── writerside.cfg
├── dotnet-tools.json
├── ext
└── devcontainer
│ ├── src
│ └── aspirate
│ │ ├── README.md
│ │ ├── devcontainer-feature.json
│ │ └── install.sh
│ └── test
│ └── aspirate
│ └── test.sh
├── nuget.config
├── release-please-config.json
├── renovate.json
├── src
├── Aspirate.Cli
│ ├── Aspirate.Cli.csproj
│ ├── AspirateCli.cs
│ ├── GlobalUsings.cs
│ ├── Middleware
│ │ └── DependencyInjectionMiddleware.cs
│ ├── Program.cs
│ ├── Properties
│ │ └── launchSettings.json
│ ├── ServiceCollectionExtensions.cs
│ ├── Templates
│ │ ├── dapr-component.hbs
│ │ ├── dashboard.hbs
│ │ ├── deployment.hbs
│ │ ├── kustomization.hbs
│ │ ├── namespace.hbs
│ │ ├── service.hbs
│ │ └── statefulset.hbs
│ └── nuget-icon.png
├── Aspirate.Commands
│ ├── Actions
│ │ ├── ActionExecutor.cs
│ │ ├── BaseAction.cs
│ │ ├── BaseActionWithNonInteractiveValidation.cs
│ │ ├── Configuration
│ │ │ ├── AskImagePullPolicyAction.cs
│ │ │ ├── AskPrivateRegistryCredentialsAction.cs
│ │ │ ├── InitializeConfigurationAction.cs
│ │ │ └── LoadConfigurationAction.cs
│ │ ├── Containers
│ │ │ ├── BuildAndPushContainersFromDockerfilesAction.cs
│ │ │ ├── BuildAndPushContainersFromProjectsAction.cs
│ │ │ └── PopulateContainerDetailsForProjectsAction.cs
│ │ ├── IAction.cs
│ │ ├── Manifests
│ │ │ ├── ApplyDaprAnnotationsAction.cs
│ │ │ ├── ApplyManifestsToClusterAction.cs
│ │ │ ├── CustomNamespaceAction.cs
│ │ │ ├── GenerateAspireManifestAction.cs
│ │ │ ├── GenerateDockerComposeManifestAction.cs
│ │ │ ├── GenerateFinalKustomizeManifestAction.cs
│ │ │ ├── GenerateHelmChartAction.cs
│ │ │ ├── GenerateKustomizeManifestsAction.cs
│ │ │ ├── IncludeAspireDashboardAction.cs
│ │ │ ├── LoadAspireManifestAction.cs
│ │ │ ├── RemoveManifestsFromClusterAction.cs
│ │ │ ├── RunKubernetesObjectsAction.cs
│ │ │ ├── StopDeployedKubernetesInstanceAction.cs
│ │ │ └── SubstituteValuesAspireManifestAction.cs
│ │ └── Secrets
│ │ │ ├── PopulateInputsAction.cs
│ │ │ └── SaveSecretsAction.cs
│ ├── Aspirate.Commands.csproj
│ ├── Commands
│ │ ├── Apply
│ │ │ ├── ApplyCommand.cs
│ │ │ ├── ApplyCommandHandler.cs
│ │ │ └── ApplyOptions.cs
│ │ ├── BaseCommand.cs
│ │ ├── BaseCommandOptions.cs
│ │ ├── BaseCommandOptionsHandler.cs
│ │ ├── Build
│ │ │ ├── BuildCommand.cs
│ │ │ ├── BuildCommandHandler.cs
│ │ │ └── BuildOptions.cs
│ │ ├── Destroy
│ │ │ ├── DestroyCommand.cs
│ │ │ ├── DestroyCommandHandler.cs
│ │ │ └── DestroyOptions.cs
│ │ ├── Generate
│ │ │ ├── GenerateCommand.cs
│ │ │ ├── GenerateCommandHandler.cs
│ │ │ └── GenerateOptions.cs
│ │ ├── GenericCommand.cs
│ │ ├── Init
│ │ │ ├── InitCommand.cs
│ │ │ ├── InitCommandHandler.cs
│ │ │ └── InitOptions.cs
│ │ ├── Run
│ │ │ ├── RunCommand.cs
│ │ │ ├── RunCommandHandler.cs
│ │ │ └── RunOptions.cs
│ │ ├── Settings
│ │ │ └── SettingsCommand.cs
│ │ └── Stop
│ │ │ ├── StopCommand.cs
│ │ │ ├── StopCommandHandler.cs
│ │ │ └── StopOptions.cs
│ ├── GlobalUsings.cs
│ ├── Options
│ │ ├── AllowClearNamespaceOption.cs
│ │ ├── AspireManifestOption.cs
│ │ ├── BaseOption.cs
│ │ ├── ComponentsOption.cs
│ │ ├── ComposeBuildsOption.cs
│ │ ├── ContainerBuildArgsOption.cs
│ │ ├── ContainerBuildContextOption.cs
│ │ ├── ContainerBuilderOption.cs
│ │ ├── ContainerImageTagOption.cs
│ │ ├── ContainerRegistryOption.cs
│ │ ├── ContainerRepositoryPrefixOption.cs
│ │ ├── DisableSecretsOption.cs
│ │ ├── DisableStateOption.cs
│ │ ├── IBaseOption.cs
│ │ ├── ImagePullPolicyOption.cs
│ │ ├── IncludeDashboardOption.cs
│ │ ├── InputPathOption.cs
│ │ ├── KubernetesContextOption.cs
│ │ ├── LaunchProfileOption.cs
│ │ ├── NamespaceOption.cs
│ │ ├── NonInteractiveOption.cs
│ │ ├── OutputFormatOption.cs
│ │ ├── OutputPathOption.cs
│ │ ├── ParameterResourceValueOption.cs
│ │ ├── PreferDockerfileOption.cs
│ │ ├── PrivateRegistryEmailOption.cs
│ │ ├── PrivateRegistryOption.cs
│ │ ├── PrivateRegistryPasswordOption.cs
│ │ ├── PrivateRegistryUrlOption.cs
│ │ ├── PrivateRegistryUsernameOption.cs
│ │ ├── ProjectPathOption.cs
│ │ ├── ReplaceSecretsOption.cs
│ │ ├── RollingRestartOption.cs
│ │ ├── RuntimeIdentifierOption.cs
│ │ ├── SecretPasswordOption.cs
│ │ ├── SkipBuildOption.cs
│ │ ├── SkipFinalKustomizeGenerationOption.cs
│ │ └── TemplatePathOption.cs
│ └── ServiceCollectionExtensions.cs
├── Aspirate.Processors
│ ├── Aspirate.Processors.csproj
│ ├── BaseResourceProcessor.cs
│ ├── FinalProcessor.cs
│ ├── GlobalUsings.cs
│ ├── Resources
│ │ ├── AbstractProcessors
│ │ │ ├── BaseContainerProcessor.cs
│ │ │ ├── ContainerProcessor.cs
│ │ │ ├── ContainerV1Processor.cs
│ │ │ ├── ParameterProcessor.cs
│ │ │ └── ValueProcessor.cs
│ │ ├── Dapr
│ │ │ ├── DaprComponentProcessor.cs
│ │ │ ├── DaprComponentTemplateData.cs
│ │ │ └── DaprProcessor.cs
│ │ ├── Dockerfile
│ │ │ └── DockerfileProcessor.cs
│ │ └── Project
│ │ │ ├── BaseProjectProcessor.cs
│ │ │ ├── ProjectProcessor.cs
│ │ │ └── ProjectV1Processor.cs
│ ├── ServiceCollectionExtensions.cs
│ └── Transformation
│ │ ├── Bindings
│ │ ├── BindingProcessor.cs
│ │ └── IBindingProcessor.cs
│ │ ├── IResourceExpressionProcessor.cs
│ │ ├── Json
│ │ ├── IJsonExpressionProcessor.cs
│ │ ├── IJsonInterpolationUnescapeProcessor.cs
│ │ ├── JsonExpressionProcessor.cs
│ │ ├── JsonInterpolation.cs
│ │ └── JsonInterpolationUnescapeProcessor.cs
│ │ ├── Literals.cs
│ │ └── ResourceExpressionProcessor.cs
├── Aspirate.Secrets
│ ├── AesGcmCrypter.cs
│ ├── Aspirate.Secrets.csproj
│ ├── GlobalUsings.cs
│ ├── MaskedValue.cs
│ ├── Protectors
│ │ ├── BaseProtector.cs
│ │ ├── ConnectionStringProtector.cs
│ │ ├── MsSqlPasswordProtector.cs
│ │ └── PostgresPasswordProtector.cs
│ ├── SecretProvider.cs
│ └── ServiceCollectionExtensions.cs
├── Aspirate.Services
│ ├── Aspirate.Services.csproj
│ ├── GlobalUsings.cs
│ ├── Implementations
│ │ ├── AspirateConfigurationService.cs
│ │ ├── AspireManifestCompositionService.cs
│ │ ├── ContainerCompositionService.cs
│ │ ├── ContainerDetailsService.cs
│ │ ├── DaprCliService.cs
│ │ ├── HelmChartCreator.cs
│ │ ├── KubeCtlService.cs
│ │ ├── KubernetesService.cs
│ │ ├── KustomizeService.cs
│ │ ├── ManifestFileParserService.cs
│ │ ├── ManifestWriter.cs
│ │ ├── PasswordGenerator.cs
│ │ ├── ProjectPropertyService.cs
│ │ ├── SecretService.cs
│ │ ├── ShellExecutionService.cs
│ │ ├── StateService.cs
│ │ └── VersionCheckService.cs
│ └── ServiceCollectionExtensions.cs
└── Aspirate.Shared
│ ├── Aspirate.Shared.csproj
│ ├── Attributes
│ └── RestorableStatePropertyAttribute.cs
│ ├── Enums
│ ├── ContainerBuilder.cs
│ ├── ExistingSecretsType.cs
│ ├── ImagePullPolicy.cs
│ ├── OutputType.cs
│ └── ProtectorType.cs
│ ├── Exceptions
│ └── ActionCausesExitException.cs
│ ├── Extensions
│ ├── AnsiConsoleExtensions.cs
│ ├── AspirateStateExtensions.cs
│ ├── ComposeExtensions.cs
│ ├── DockerfileParametersExtensions.cs
│ ├── JsonExtensions.cs
│ ├── KubernetesDeploymentDataExtensions.cs
│ ├── PathExtensions.cs
│ ├── ProtectionStrategyExtensions.cs
│ ├── ResourceExtensions.cs
│ └── StringExtensions.cs
│ ├── GlobalUsings.cs
│ ├── Inputs
│ ├── BaseCreateOptions.cs
│ ├── BaseKubernetesCreateOptions.cs
│ ├── ContainerOptions.cs
│ ├── CreateComposeEntryOptions.cs
│ ├── CreateKubernetesObjectsOptions.cs
│ ├── CreateManifestsOptions.cs
│ ├── KubernetesRunOptions.cs
│ ├── SecretManagementOptions.cs
│ ├── ShellCommandOptions.cs
│ └── StateManagementOptions.cs
│ ├── Interfaces
│ ├── Commands
│ │ ├── Contracts
│ │ │ ├── IApplyOptions.cs
│ │ │ ├── IAspireOptions.cs
│ │ │ ├── IBuildOptions.cs
│ │ │ ├── IComponentsOptions.cs
│ │ │ ├── IContainerOptions.cs
│ │ │ ├── IDashboardOptions.cs
│ │ │ ├── IGenerateOptions.cs
│ │ │ ├── IInitOptions.cs
│ │ │ ├── IKubernetesOptions.cs
│ │ │ ├── IPrivateRegistryCredentialsOptions.cs
│ │ │ ├── IRunOptions.cs
│ │ │ └── ISecretState.cs
│ │ ├── ICommandOptions.cs
│ │ └── ICommandOptionsHandler.cs
│ ├── Processors
│ │ ├── IImageProcessor.cs
│ │ └── IResourceProcessor.cs
│ ├── Secrets
│ │ ├── IDecrypter.cs
│ │ ├── IEncrypter.cs
│ │ ├── IPasswordGenerator.cs
│ │ └── ISecretProvider.cs
│ └── Services
│ │ ├── IAspirateConfigurationService.cs
│ │ ├── IAspireManifestCompositionService.cs
│ │ ├── IContainerCompositionService.cs
│ │ ├── IContainerDetailsService.cs
│ │ ├── IDaprCliService.cs
│ │ ├── IHelmChartCreator.cs
│ │ ├── IKubeCtlService.cs
│ │ ├── IKubernetesService.cs
│ │ ├── IKustomizeService.cs
│ │ ├── IManifestFileParserService.cs
│ │ ├── IManifestWriter.cs
│ │ ├── IProjectPropertyService.cs
│ │ ├── IProtectionStrategy.cs
│ │ ├── ISecretService.cs
│ │ ├── IShellExecutionService.cs
│ │ ├── IStateService.cs
│ │ └── IVersionCheckService.cs
│ ├── Literals
│ ├── AspirateLiterals.cs
│ ├── AspirateSecretLiterals.cs
│ ├── AspireComponentLiterals.cs
│ ├── AspireLiterals.cs
│ ├── ContainerBuilderLiterals.cs
│ ├── DockerLiterals.cs
│ ├── DotNetSdkLiterals.cs
│ ├── EmojiLiterals.cs
│ ├── KubeCtlLiterals.cs
│ ├── MsBuildPropertiesLiterals.cs
│ └── TemplateLiterals.cs
│ ├── Models
│ ├── ArgumentsBuilder.cs
│ ├── Aspirate
│ │ ├── AspirateContainerSettings.cs
│ │ ├── AspirateSettings.cs
│ │ ├── AspirateState.cs
│ │ ├── ComposeService.cs
│ │ ├── KubernetesDeploymentData.cs
│ │ ├── LastVersionChecked.cs
│ │ └── SecretState.cs
│ ├── AspireManifests
│ │ ├── AspireDashboard.cs
│ │ ├── Components
│ │ │ ├── Common
│ │ │ │ ├── Binding.cs
│ │ │ │ ├── Build.cs
│ │ │ │ ├── Container
│ │ │ │ │ └── ContainerResourceBase.cs
│ │ │ │ ├── ProjectResource.cs
│ │ │ │ └── Volume.cs
│ │ │ ├── UnsupportedResource.cs
│ │ │ ├── V0
│ │ │ │ ├── Container
│ │ │ │ │ └── ContainerResource.cs
│ │ │ │ ├── Dapr
│ │ │ │ │ ├── ComponentMetadata.cs
│ │ │ │ │ ├── DaprComponentResource.cs
│ │ │ │ │ ├── DaprResource.cs
│ │ │ │ │ └── InnerDaprComponent.cs
│ │ │ │ ├── DockerfileResource.cs
│ │ │ │ ├── Parameters
│ │ │ │ │ ├── Generate.cs
│ │ │ │ │ ├── ParameterDefault.cs
│ │ │ │ │ ├── ParameterInput.cs
│ │ │ │ │ ├── ParameterResource.cs
│ │ │ │ │ └── ValueResource.cs
│ │ │ │ └── Ports.cs
│ │ │ └── V1
│ │ │ │ └── Container
│ │ │ │ └── ContainerV1Resource.cs
│ │ ├── Interfaces
│ │ │ ├── IResource.cs
│ │ │ ├── IResourceWithAnnotations.cs
│ │ │ ├── IResourceWithArgs.cs
│ │ │ ├── IResourceWithBinding.cs
│ │ │ ├── IResourceWithConnectionString.cs
│ │ │ ├── IResourceWithEnvironmentalVariables.cs
│ │ │ ├── IResourceWithInput.cs
│ │ │ ├── IResourceWithParent.cs
│ │ │ ├── IResourceWithValue.cs
│ │ │ └── IResourceWithVolumes.cs
│ │ └── Resource.cs
│ ├── Compose
│ │ ├── ComposeService.cs
│ │ └── ComposeServiceBuilder.cs
│ ├── Kubernetes
│ │ ├── DockerAuth.cs
│ │ ├── DockerAuthSecretData.cs
│ │ ├── DockerConfigJson.cs
│ │ ├── ImagePullSecret.cs
│ │ ├── Secret.cs
│ │ ├── SecretData.cs
│ │ └── SecretMetadata.cs
│ └── MsBuild
│ │ ├── BaseMsBuildProperties.cs
│ │ ├── MsBuildContainerProperties.cs
│ │ └── MsBuildProperties.cs
│ └── Outputs
│ ├── CommandAvailableResult.cs
│ └── ShellCommandResult.cs
└── tests
└── Aspirate.Tests
├── ActionsTests
├── ActionExecutorTests.cs
├── BaseActionTests.cs
├── Configuration
│ ├── InitializeConfigurationActionTests.cs
│ ├── LoadConfigurationActionTests.cs
│ └── VerifyResults
│ │ └── InitializeConfigurationActionTests.ExecuteInitializeConfigurationAction_InteractiveMode_Success.verified.txt
├── Containers
│ └── PopulateContainerDetailsForProjectsActionTests.cs
├── Manifests
│ ├── ApplyManifestsToClusterActionTests.cs
│ ├── GenerateAspireManifestActionTests.cs
│ ├── GenerateDockerComposeManifestActionTests.cs
│ ├── GenerateKustomizeManifestsActionTests.cs
│ ├── LoadAspireManifestActionTests.cs
│ └── RemoveManifestsToClusterActionTests.cs
└── Secrets
│ ├── PopulateInputsActionTests.cs
│ └── SaveSecretsActionTests.cs
├── Aspirate.Tests.csproj
├── AspirateTestBase.cs
├── DockerComposeTests
├── DockerComposeBuilderTests.cs
└── VerifyResults
│ ├── DockerComposeBuilderTests.SerializeComposeFile_Empty_ShouldBeValid.verified.txt
│ ├── DockerComposeBuilderTests.SerializeComposeFile_WithBuildArguments_ShouldBeValid.verified.txt
│ ├── DockerComposeBuilderTests.SerializeComposeFile_WithDockerfile_ShouldBeValid.verified.txt
│ ├── DockerComposeBuilderTests.SerializeComposeFile_WithExposedPort_ShouldBeValid.verified.txt
│ ├── DockerComposeBuilderTests.SerializeComposeFile_WithExposedPorts_ShouldBeValid.verified.txt
│ ├── DockerComposeBuilderTests.SerializeComposeFile_WithImage_ShouldBeValid.verified.txt
│ └── DockerComposeBuilderTests.SerializeComposeFile_WithPrivillegedService_ShouldBeValid.verified.txt
├── ExtensionTests
└── KubernetesDeploymentDataExtensionTests.cs
├── GlobalUsings.cs
├── ProcessorTests
├── ContainerProcessorTests.cs
└── JsonInterpolationTests.cs
├── SecretTests
├── MaskedValueTests.cs
└── SecretProviderTests.cs
├── ServiceTests
├── AspirateConfigurationServiceTest.cs
├── BaseServiceTests.cs
├── ContainerCompositionServiceTest.cs
├── ContainerDetailsServiceTests.cs
├── ContainerOptionsTests.cs
├── KubeCtlServiceTests.cs
├── ManifestFileParserServiceTests.cs
├── ProjectPropertyServiceTests.cs
├── SecretServiceTests.cs
└── VerifyResults
│ ├── AspirateConfigurationServiceTest.LoadConfigurationFile_ReturnsExpectedObject_WhenConfigurationFileExists.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=FullResponse.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=FullResponseWithDockerfileAndContext.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=FullResponseWithPrefix.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=NoImageOrTagShouldBeRepositoryLatest.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=NoImageOrTagShouldBeRepositoryWithPrefixLatest.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=NoImageShouldBeRepository.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=NoImageShouldBeRepositoryWithPrefix.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=NoRegistryOrRepositoryOrTagShouldBeImageLatest.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=NoRepositoryOrTagShouldBeImageLatest.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=NoRepositoryShouldBeImage.verified.txt
│ ├── ContainerDetailsServiceTests.GetContainerDetails_WhenCalled_ReturnsCorrectContainerDetails_properties=NoTagShouldBeLatest.verified.txt
│ ├── ContainerOptionsTests.ContainerParametersFullyPopulated_ShouldPopulateImageCorrectly_testOptions=FullParameters.verified.txt
│ ├── ContainerOptionsTests.ContainerParametersFullyPopulated_ShouldPopulateImageCorrectly_testOptions=ImageAndTag.verified.txt
│ └── ContainerOptionsTests.ContainerParametersFullyPopulated_ShouldPopulateImageCorrectly_testOptions=RegistryAndPrefixAndImage.verified.txt
└── TestData
├── Program.cs
├── TestApp.csproj
├── connectionstring-resource-expression.json
├── nodejs.json
├── pg-endtoend.json
├── project-no-binding.json
├── shop.json
├── sqlserver-endtoend.json
├── starter-with-db.json
├── starter-with-redis.json
└── with-unsupported-resource.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################
2 | # Git Line Endings #
3 | ###############################
4 |
5 | # Set default behavior to automatically normalize line endings.
6 | * text=auto
7 |
8 | # Force batch scripts to always use CRLF line endings so that if a repo is accessed
9 | # in Windows via a file share from Linux, the scripts will work.
10 | *.{cmd,[cC][mM][dD]} text eol=crlf
11 | *.{bat,[bB][aA][tT]} text eol=crlf
12 |
13 | # Force bash scripts to always use LF line endings so that if a repo is accessed
14 | # in Unix via a file share from Windows, the scripts will work.
15 | *.sh text eol=lf
16 |
17 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🔥 Bug
3 | about: Something that needs fixing.
4 | title: ""
5 | labels: "Type: Bug"
6 | assignees: ""
7 |
8 | ---
9 |
10 | ### 🔥 Bug Description
11 | A clear and concise description of what the bug is.
12 |
13 | ### 🔍 Steps to Reproduce the Bug
14 | 1. Go to '...'
15 | 2. Click on '....'
16 | 3. Scroll down to '....'
17 | 4. See error
18 |
19 | ### 🧯 Possible Solution
20 | * Usefull tips or places to start go here.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/user-story.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🚀 Technical Story
3 | about: An improvement to the project from a user or technical perspective.
4 | title: ""
5 | labels: "Type: Story"
6 | assignees: ""
7 |
8 | ---
9 |
10 | ### 🚀 Feature Description
11 | As a [user role], I'd like to [improvement description], in order to [value this improvement adds].
12 |
13 | ### ✔ Goals
14 | - [ ] Create a button for X in Y
15 | - [ ] Add an input field for Z
16 | - [ ] Refactor class W
17 |
18 | ### 🧰 Possible Solution
19 | - Usefull tips or places to start go here.
20 |
21 | ### 🚧 Blocked by
22 | - #666 - creation of fields in X
23 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ### 🚀 Feature Description
2 | This PR introduces [feature description]. This resolves #1337.
3 |
4 | ### 🧰 Technical Solution
5 | - [x] Change this to that
6 | - [x] Add something in some file
7 | - [x] Add a library to do some magic
8 |
9 | ### ⚠️ Known Issues
10 | - Introduces some weird bug (#666) which needs to be fixed before merging this!
11 | - All documents are now missing (#404)
12 |
13 | =======================================================
14 |
15 | ### 🧯 Bugfix Description
16 | This PR fixes [bug description]. This resolves #1234.
17 |
18 | ### 🧰 Technical Solution
19 | - [x] Change this to that
20 | - [x] Add something in some file
21 |
22 | =======================================================
23 |
24 | ### ⚠️ Breaking Changes
25 | - #500 - Public api endpoint returns different structure
26 | - #501 - Public method signature changed in some file
27 |
28 | ### 🚀 New Features
29 | - #337 - Fancy new feature 1
30 | - #666 - Feature 2
31 |
32 | ### 🧯 Bugfixes
33 | - #666 - Fixed random crashes
34 | - #404 - Documents can now be found again
35 |
--------------------------------------------------------------------------------
/.github/workflows/conventional-commits.yaml:
--------------------------------------------------------------------------------
1 | name: Conventional Commits
2 |
3 | on:
4 | pull_request:
5 | branches: [ main ]
6 |
7 | jobs:
8 | build:
9 | name: Conventional Commits
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v4.2.2
13 | - uses: webiny/action-conventional-commits@v1.3.0
14 | with:
15 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/devcontainer-release.yaml:
--------------------------------------------------------------------------------
1 | name: Release dev container image
2 |
3 | env:
4 | IMAGE_NAME: ${{ github.repository }}
5 | REGISTRY: ghcr.io
6 |
7 | on:
8 | workflow_dispatch:
9 |
10 | jobs:
11 | call-workflow-test:
12 | uses: ./.github/workflows/devcontainer-test.yaml
13 |
14 | deploy:
15 | needs:
16 | - call-workflow-test
17 | if: ${{ github.ref == 'refs/heads/main' }}
18 | runs-on: ubuntu-latest
19 | permissions:
20 | contents: write
21 | packages: write
22 | pull-requests: write
23 | environment:
24 | name: deploy-devcontainer-feature
25 | steps:
26 | - uses: actions/checkout@v3
27 |
28 | - name: "Publish Features"
29 | uses: devcontainers/action@v1
30 | with:
31 | publish-features: "true"
32 | base-path-to-features: "./ext/devcontainer/src"
33 | generate-docs: "false"
34 |
35 | env:
36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/devcontainer-test.yaml:
--------------------------------------------------------------------------------
1 | name: Test dev container
2 | on:
3 | workflow_dispatch:
4 | pull_request:
5 | paths:
6 | - "ext/devcontainer/**"
7 | - ".github/workflows/devcontainer-test.yaml"
8 | branches: [main]
9 | workflow_call:
10 |
11 | jobs:
12 | test:
13 | runs-on: ubuntu-latest
14 | strategy:
15 | fail-fast: false
16 | matrix:
17 | baseImage:
18 | [
19 | "mcr.microsoft.com/devcontainers/dotnet",
20 | ]
21 | steps:
22 | - uses: actions/checkout@v3
23 |
24 | - name: "Install latest devcontainer CLI"
25 | run: npm install -g @devcontainers/cli
26 |
27 | - name: "Generating tests for 'aspirate' against '${{ matrix.baseImage }}'"
28 | run: devcontainer features test -f aspirate -i ${{ matrix.baseImage }} .
29 | working-directory: ./ext/devcontainer
30 |
31 | test-scenarios:
32 | runs-on: ubuntu-latest
33 | steps:
34 | - uses: actions/checkout@v3
35 |
36 | - name: "Install latest devcontainer CLI"
37 | run: npm install -g @devcontainers/cli
38 |
39 | - name: "Testing 'aspirate' scenarios"
40 | run: devcontainer features test -f aspirate --skip-autogenerated .
41 | working-directory: ./ext/devcontainer
42 |
43 | test-global:
44 | runs-on: ubuntu-latest
45 | steps:
46 | - uses: actions/checkout@v3
47 |
48 | - name: "Install latest devcontainer CLI"
49 | run: npm install -g @devcontainers/cli
50 |
51 | - name: "Testing global scenarios"
52 | run: devcontainer features test --global-scenarios-only .
53 | working-directory: ./ext/devcontainer
--------------------------------------------------------------------------------
/.github/workflows/main.yaml:
--------------------------------------------------------------------------------
1 | name: Main CI-CD
2 |
3 | env:
4 | NUGET_SERVER: https://api.nuget.org/v3/index.json
5 | NUGET_API_KEY: ${{ secrets.PUBLIC_NUGET_TOKEN }}
6 | DOTNET_NOLOGO: true
7 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
8 | DOTNET_CLI_TELEMETRY_OPTOUT: true
9 | CAKE_SETTINGS_SKIPVERIFICATION: true
10 |
11 | on:
12 | push:
13 | tags:
14 | - "*"
15 |
16 | jobs:
17 | build-test-pull-request:
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | - uses: actions/checkout@v3
22 | with:
23 | fetch-depth: 0
24 |
25 | - uses: actions/setup-dotnet@v3
26 | with:
27 | dotnet-version: '9.0.x'
28 |
29 | - name: Build, Test and Pack
30 | run: |
31 | dotnet tool restore
32 | dotnet cake --pack true --settings_skipverification=true
33 |
34 | - name: Push to nuget
35 | working-directory: artifacts/
36 | run: dotnet nuget push *.nupkg -s $NUGET_SERVER -k $NUGET_API_KEY
--------------------------------------------------------------------------------
/.github/workflows/pull-requests.yaml:
--------------------------------------------------------------------------------
1 | name: Pull Requests
2 |
3 | env:
4 | DOTNET_NOLOGO: true
5 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
6 | DOTNET_CLI_TELEMETRY_OPTOUT: true
7 | CAKE_SETTINGS_SKIPVERIFICATION: true
8 |
9 | on:
10 | - pull_request
11 |
12 | jobs:
13 | build-test-pull-request:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v3
18 | with:
19 | fetch-depth: 0
20 |
21 | - uses: actions/setup-dotnet@v3
22 | with:
23 | dotnet-version: '9.0.x'
24 |
25 | - name: Build and Test
26 | run: |
27 | dotnet tool restore
28 | dotnet cake
29 |
--------------------------------------------------------------------------------
/.github/workflows/release-please.yaml:
--------------------------------------------------------------------------------
1 | name: "Release Please"
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | workflow_dispatch:
8 |
9 | permissions:
10 | contents: write
11 | pull-requests: write
12 |
13 | jobs:
14 | release-please:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: googleapis/release-please-action@v4
18 | with:
19 | token: ${{ secrets.RELEASE_PLEASE_TOKEN }}
--------------------------------------------------------------------------------
/.release-please-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | ".": "9.1.0"
3 | }
4 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | $(NoWarn);NU5104
6 | true
7 | true
8 | true
9 | $(OutputPath)$(AssemblyName).xml
10 | true
11 | enable
12 | enable
13 | false
14 | true
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Directory.Build.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(OutputPath)$(AssemblyName).xml
4 |
5 |
--------------------------------------------------------------------------------
/LICENSE.TXT:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) David Sekula
4 |
5 | All rights reserved.
6 |
7 | Permission is hereby granted, free of charge, to any person obtaining a copy
8 | of this software and associated documentation files (the "Software"), to deal
9 | in the Software without restriction, including without limitation the rights
10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | copies of the Software, and to permit persons to whom the Software is
12 | furnished to do so, subject to the following conditions:
13 |
14 | The above copyright notice and this permission notice shall be included in all
15 | copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | SOFTWARE.
--------------------------------------------------------------------------------
/docs/Writerside/c.list:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/Writerside/hi.tree:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/docs/Writerside/redirection-rules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 | Created after removal of "Environmental Variables" from Aspir8: Aspire to Deployments
11 | Environmental-Variables.html
12 |
13 |
--------------------------------------------------------------------------------
/docs/Writerside/topics/Apply-Manifests.md:
--------------------------------------------------------------------------------
1 | # Applying Manifests to a Cluster
2 |
3 | To apply the manifests to your cluster, run:
4 |
5 | ```bash
6 | aspirate apply
7 | ```
8 |
9 | You will first be presented with all the context names (unless you have passed one in as a cli option) in your kubeconfig file, and will be asked to select one.
10 | This will be used for deployment
11 |
12 | If you have a secret file, you will be prompted to enter the password to decrypt it.
13 |
14 | ## Cli Options (Optional)
15 |
16 | | Option | Alias | Environmental Variable Counterpart | Description |
17 | |-------------------|-------|------------------------------------|-----------------------------------------------------------------------------------------------------|
18 | | --input-path | -i | `ASPIRATE_INPUT_PATH` | The path for the kustomize manifests directory. Defaults to `%output-dir%` |
19 | | --kube-context | -k | `ASPIRATE_KUBERNETES_CONTEXT` | The name of the kubernetes context within your kubeconfig to apply / deploy manifests to. |
20 | | --secret-password | | `ASPIRATE_SECRET_PASSWORD` | If using secrets, or you have a secret file - Specify the password to decrypt them |
21 | | --non-interactive | | `ASPIRATE_NON_INTERACTIVE` | Disables interactive mode for the command |
22 | | --disable-secrets | | `ASPIRATE_DISABLE_SECRETS` | Disables secrets management features. |
23 | | --rolling-restart | -r | `ASPIRATE_ROLLING_RESTART` | Perform a rollout restart of deployments directly after applying the manifests. Defaults to `false` |
--------------------------------------------------------------------------------
/docs/Writerside/topics/Applying-Secrets.md:
--------------------------------------------------------------------------------
1 | # Applying Secrets
2 |
3 | The `apply` command is used to apply the manifests to the cluster.
4 |
5 | See [Applying Manifests to a Cluster](Apply-Manifests.md) for more information.
--------------------------------------------------------------------------------
/docs/Writerside/topics/Commands.md:
--------------------------------------------------------------------------------
1 | # Commands
2 |
3 | > **Note**
4 | >
5 | > For best results - %product% should be run from the root of your aspire project (AppHost directory).
6 | >
7 | {style="note"}
8 |
9 | %product% is a Cli-driven application, and as such, has a number of commands that can be used to perform various tasks.
10 |
11 | The pages in this section will go into detail about each command, and how to use them.
12 | All the cli options listed in tables on each page are optional, and will default to the values allowing %product% prompting the user for input.
--------------------------------------------------------------------------------
/docs/Writerside/topics/Disable-Secret-Management.md:
--------------------------------------------------------------------------------
1 | # Disable Secret Management
2 |
3 | If secrets are not required, the `--disable-secrets` flag can be passed to all commands to disable secret functionality.
4 |
5 | However, once manifests are generated with secrets included, they cannot be disabled without regenerating the manifests.
6 |
7 | ## Example
8 |
9 | ```bash
10 | aspirate generate --disable-secrets
11 | ```
--------------------------------------------------------------------------------
/docs/Writerside/topics/General-FAQ.md:
--------------------------------------------------------------------------------
1 | # General / FAQ
2 |
3 | ## Configuring the Windows Terminal For Unicode and Emoji Support
4 |
5 | Windows Terminal supports Unicode and Emoji. However, the shells such as Powershell and cmd.exe do not.
6 | For the difference between the two,
7 | see [What's the difference between a console,
8 | a terminal and a shell](https://www.hanselman.com/blog/whats-the-difference-between-a-console-a-terminal-and-a-shell).
9 |
10 | For PowerShell, the following command will enable Unicode and Emoji support. You can add this to your `profile.ps1`
11 | file:
12 |
13 | ```bash
14 | [console]::InputEncoding = [console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
15 | ```
16 |
17 | For cmd.exe, the following steps are required to enable Unicode and Emoji support.
18 |
19 | 1. Run `intl.cpl`.
20 | 2. Click the Administrative tab
21 | 3. Click the Change system locale button.
22 | 4. Check the "Use Unicode UTF-8 for worldwide language support" checkbox.
23 | 5. Reboot.
24 |
25 | You will also need to ensure that your Console application is configured to use a font that supports Unicode and Emoji,
26 | such as Cascadia Code.
27 |
--------------------------------------------------------------------------------
/docs/Writerside/topics/Generating-Secrets.md:
--------------------------------------------------------------------------------
1 | # Generating Secrets
2 |
3 | The `generate` command is used to generate secrets for the application.
4 | After containers and projects have been build and pushed, if there are any protectable configuration values within the aspire manifest, secret generation will commence.
5 |
6 | If you do not already have existing secrets, you will be prompted to enter a password to encrypt the secrets with.
7 | You will have to enter this twice, once to confirm the password.
8 | This password will be used to encrypt the secrets, and parameter resources and will be required to decrypt them. Please keep this password safe.
9 |
10 | If you already have existing secrets, you will be prompted to enter the password used to encrypt them.
11 | You have three chances to enter the correct password, after which the generation process will be aborted.
12 |
13 | If you already have existing secrets, they will be reused unless `--replace-secrets` is passed as a cli option.
14 |
15 | After secrets have been generated, they will be encrypted and stored in the `aspirate-state.json` file.
16 | The `generate` command will then move on to manifest generation.
--------------------------------------------------------------------------------
/docs/Writerside/topics/Getting-Started.md:
--------------------------------------------------------------------------------
1 | # Aspir8
2 |
3 | 
4 |
5 | [](https://www.nuget.org/packages/aspirate/)
6 | [](https://www.nuget.org/packages/aspirate/)
7 |
8 | Generate deployment yaml for a .NET Aspire AppHost project.
9 |
10 |
11 |
--------------------------------------------------------------------------------
/docs/Writerside/topics/Installing-as-a-Global-Tool.md:
--------------------------------------------------------------------------------
1 | # Installing as a Global Tool
2 |
3 | > **Note**
4 | >
5 | > While %product% is in development, the package will be versioned as a preview and the `--prelease` option will get the latest preview.
6 | >
7 | {style="note"}
8 |
9 | %product% is shipped to nuget as a .NET Core Global Tool, which means you can install it with a single command:
10 |
11 | ```bash
12 | dotnet tool install -g aspirate --prerelease
13 | ```
14 |
15 | Alternatively, if you already have %product% installed, you can update it to the latest version using the following command:
16 |
17 | ```bash
18 | dotnet tool update -g aspirate --prerelease
19 | ```
--------------------------------------------------------------------------------
/docs/Writerside/topics/Managing-Secrets.md:
--------------------------------------------------------------------------------
1 | # Managing Secrets
2 |
3 | During the `generate` and `apply` processes, you will be prompted to input a password.
4 | This password is used to encrypt your secrets in the state file, named aspirate-state.json
5 |
6 | > **Note**
7 | >
8 | > This password is not stored anywhere, and is only used to encrypt and decrypt the secrets within the state file.
9 | > If you lose this password, you will be unable to access your secrets and will need to use the `generate` command to create a new one.
10 | {style="warning"}
11 |
--------------------------------------------------------------------------------
/docs/Writerside/topics/Non-Interactive-Invocation.md:
--------------------------------------------------------------------------------
1 | # Non-Interactive Invocation
2 |
3 | All can be invoked non-interactively by passing the `--non-interactive` flag.
4 |
5 | This will cause the tool to use the default context and not prompt for confirmation.
6 |
7 | When using this flag, all configuration arguments must be passed on the command line.
8 |
9 | `apply` and `destroy` will be invoked non-interactively by default if all required arguments are passed.
10 |
11 | ## Examples
12 |
13 | ```bash
14 | aspirate build --non-interactive -m ./manifest.json
15 | ```
16 |
17 | ```bash
18 | aspirate generate --non-interactive -m ./manifest.json --skip-build --secret-password someSecret --image-pull-policy IfNotPresent
19 | ```
20 |
21 | ```bash
22 | aspirate apply --non-interactive --secret-password someSecret -k docker-desktop
23 | or
24 | aspirate apply --secret-password someSecret -k docker-desktop
25 | ```
26 |
27 | ```bash
28 | aspirate destroy --non-interactive -k docker-desktop
29 | or
30 | aspirate destroy -k docker-desktop
31 | ```
32 |
--------------------------------------------------------------------------------
/docs/Writerside/topics/Removing-Aspirate.md:
--------------------------------------------------------------------------------
1 | # Removal / Uninstallation
2 |
3 | Because Aspirate ships as a .NET Core Global Tool, it can be removed using a single command:
4 |
5 | ```bash
6 | dotnet tool uninstall -g aspirate
7 | ```
--------------------------------------------------------------------------------
/docs/Writerside/topics/Removing-Manifests-from-a-Cluster.md:
--------------------------------------------------------------------------------
1 | # Removing Manifests from a Cluster
2 |
3 | To remove the manifests from your cluster, run:
4 |
5 | ```bash
6 | aspirate destroy
7 | ```
8 |
9 | You will first be presented with all the context names (unless you have passed one in as a cli option) in your kubeconfig file, and will be asked to select one.
10 | This will be used for removal.
11 |
12 | If you have a secret file, these secrets will be removed as well. This command does not prompt for a password, as secrets do not need to be decrypted to be removed from your cluster.
13 |
14 | ## Cli Options (Optional)
15 |
16 | | Option | Alias | Environmental Variable Counterpart | Description |
17 | |-------------------|-------|------------------------------------|-------------------------------------------------------------------------------------------|
18 | | --input-path | -i | `ASPIRATE_INPUT_PATH` | The path for the kustomize manifests directory. Defaults to `%output-dir%` |
19 | | --kube-context | -k | `ASPIRATE_KUBERNETES_CONTEXT` | The name of the kubernetes context within your kubeconfig to apply / deploy manifests to. |
20 | | --non-interactive | | `ASPIRATE_NON_INTERACTIVE` | Disables interactive mode for the command |
21 | | --disable-secrets | | `ASPIRATE_DISABLE_SECRETS` | Disables secrets management features. |
--------------------------------------------------------------------------------
/docs/Writerside/topics/Secret-Management.md:
--------------------------------------------------------------------------------
1 | # Secret Management
2 |
3 | Aspirate now includes built-in support for robust secret management, allowing you to easily encrypt sensitive data such as connection strings.
4 | This feature is designed to increase security and minimize vulnerabilities.
5 |
6 | Aspir8 which uses AesGcm encryption to encrypt the secret's file using a password.
7 | The user supplies this password during the `generate` and `apply` processes.
8 | It's generated using Pbkdf2 with SHA256, one million iterations, and the hash and salt are stored in the secret file.
9 | Secrets protected by this provider are only accessible to users who know the password, and are completely safe to store in a Git repository.
--------------------------------------------------------------------------------
/docs/Writerside/v.list:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/docs/Writerside/writerside.cfg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "cake.tool": {
6 | "version": "4.0.0",
7 | "commands": [
8 | "dotnet-cake"
9 | ]
10 | },
11 | "minver-cli": {
12 | "version": "4.3.0",
13 | "commands": [
14 | "minver"
15 | ]
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/ext/devcontainer/src/aspirate/README.md:
--------------------------------------------------------------------------------
1 | # Aspir8 (aspirate)
2 |
3 | Installs [Aspir8](https://github.com/prom3theu5/aspirational-manifests)
4 |
5 | > **NOTE**
6 | >
7 | > Requires `ghcr.io/devcontainers/features/dotnet:2` or a base image with dotnet installed.
8 |
9 | ## Example Usage - Install current latest `aspirate` version **without dotnet base image**
10 |
11 | ```json
12 | "features": {
13 | "ghcr.io/devcontainers/features/dotnet:2": {}
14 | "ghcr.io/prom3theu5/aspirational-manifests/aspirate:latest": {}
15 | }
16 | ```
17 |
18 | ## Example Usage - Install current latest `aspirate` version **with dotnet base image**
19 |
20 | ```json
21 | "features": {
22 | "ghcr.io/prom3theu5/aspirational-manifests/aspirate:latest": {}
23 | }
24 | ```
--------------------------------------------------------------------------------
/ext/devcontainer/src/aspirate/devcontainer-feature.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "aspirate",
3 | "version": "0.1.0",
4 | "name": "Aspir8 Aspirate to Kubernetes",
5 | "documentationURL": "https://prom3theu5.github.io/aspirational-manifests/",
6 | "description": "Installs Aspir8.",
7 | "installsAfter": [
8 | "ghcr.io/devcontainers/features/dotnet"
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/ext/devcontainer/src/aspirate/install.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | echo "(*) Installing Aspir8"
4 |
5 | dotnet tool update aspirate --prerelease --tool-path /usr/local/bin
--------------------------------------------------------------------------------
/ext/devcontainer/test/aspirate/test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | # Optional: Import test library
6 | source dev-container-features-test-lib
7 |
8 | # Definition specific tests
9 | check "version" aspirate --version
10 |
11 | # Report result
12 | reportResults
13 |
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/release-please-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
3 | "packages": {
4 | ".": {
5 | "changelog-path": "CHANGELOG.md",
6 | "release-type": "simple",
7 | "extra-files": ["Directory.Build.props"],
8 | "changelog-sections": [
9 | { "type": "feat", "section": "🚀 New Features", "hidden": false },
10 | { "type": "feature", "section": "🚀 New Features", "hidden": false },
11 | { "type": "enhance", "section": "💅 Enhancements", "hidden": false },
12 | { "type": "fix", "section": "🔥 Bug Fixes", "hidden": false },
13 | { "type": "perf", "section": "🏃 Performance Improvements", "hidden": false },
14 | { "type": "revert", "section": "↩️ Reverts", "hidden": true },
15 | { "type": "docs", "section": "📚 Documentation", "hidden": false },
16 | { "type": "style", "section": "🎨 Code Style", "hidden": false },
17 | { "type": "chore", "section": "⚙️ Chores", "hidden": false },
18 | { "type": "refactor", "section": "⌨️ Code Refactoring", "hidden": false },
19 | { "type": "test", "section": "🧪 Automated Testing", "hidden": false },
20 | { "type": "build", "section": "🛠️ Build System", "hidden": false },
21 | { "type": "ci", "section": "📦 CI Improvements", "hidden": false }
22 | ]
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "assigneesFromCodeOwners": true,
6 | "ignoreDeps": ["Abp"],
7 | "packageRules": [
8 | {
9 | "matchPackagePatterns": [
10 | "*"
11 | ],
12 | "matchUpdateTypes": [
13 | "minor",
14 | "patch"
15 | ],
16 | "groupName": "all non-major dependencies",
17 | "groupSlug": "all-minor-patch",
18 | "automerge": false,
19 | "labels": [
20 | "dependencies"
21 | ]
22 | },
23 | {
24 | "matchPackagePatterns": [
25 | "*"
26 | ],
27 | "matchUpdateTypes": [
28 | "major"
29 | ],
30 | "labels": [
31 | "dependencies",
32 | "breaking"
33 | ]
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/src/Aspirate.Cli/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | // Global using directives
2 |
3 | global using System.CommandLine;
4 | global using System.CommandLine.Builder;
5 | global using System.CommandLine.Invocation;
6 | global using System.CommandLine.NamingConventionBinder;
7 | global using System.CommandLine.Parsing;
8 | global using Microsoft.Extensions.DependencyInjection;
9 | global using Microsoft.Extensions.DependencyInjection.Extensions;
10 |
11 | global using Aspirate.Cli;
12 | global using Aspirate.Cli.Middleware;
13 | global using Aspirate.Commands;
14 | global using Aspirate.Commands.Commands.Apply;
15 | global using Aspirate.Commands.Commands.Build;
16 | global using Aspirate.Commands.Commands.Destroy;
17 | global using Aspirate.Commands.Commands.Generate;
18 | global using Aspirate.Commands.Commands.Init;
19 | global using Aspirate.Commands.Commands.Run;
20 | global using Aspirate.Commands.Commands.Settings;
21 | global using Aspirate.Commands.Commands.Stop;
22 | global using Aspirate.Processors;
23 | global using Aspirate.Secrets;
24 | global using Aspirate.Services;
25 | global using Aspirate.Shared.Literals;
26 | global using Spectre.Console;
27 |
--------------------------------------------------------------------------------
/src/Aspirate.Cli/Middleware/DependencyInjectionMiddleware.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Cli.Middleware;
2 |
3 | internal static class DependencyInjectionMiddleware
4 | {
5 | public static CommandLineBuilder UseDependencyInjection(this CommandLineBuilder builder, Action configureServices) =>
6 | UseDependencyInjection(builder, (_, services) => configureServices(services));
7 |
8 | private static CommandLineBuilder UseDependencyInjection(this CommandLineBuilder builder, Action configureServices) =>
9 | builder.AddMiddleware((context, next) =>
10 | {
11 | var services = new ServiceCollection();
12 | configureServices(context, services);
13 |
14 | services.TryAddSingleton(context.Console);
15 |
16 | context.BindingContext.AddService(_ => services);
17 |
18 | return next(context);
19 | });
20 | }
21 |
--------------------------------------------------------------------------------
/src/Aspirate.Cli/Program.cs:
--------------------------------------------------------------------------------
1 | AspirateCli.WelcomeMessage();
2 |
3 | return await new CommandLineBuilder(new AspirateCli())
4 | .UseDefaults()
5 | .UseDependencyInjection(services => services.RegisterAspirateEssential())
6 | .UseHelp(AspirateCli.UseDefaultMasking)
7 | .Build()
8 | .InvokeAsync(args);
9 |
--------------------------------------------------------------------------------
/src/Aspirate.Cli/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Cli;
2 |
3 | internal static class ServiceCollectionExtensions
4 | {
5 | internal static void RegisterAspirateEssential(this IServiceCollection services) =>
6 | services
7 | .AddSpectreConsole()
8 | .AddSecretProtectionStrategies()
9 | .AddAspirateState()
10 | .AddAspirateServices()
11 | .AddAspirateActions()
12 | .AddAspirateProcessors()
13 | .AddAspirateSecretProvider()
14 | .AddPlaceholderTransformation();
15 |
16 | private static IServiceCollection AddSpectreConsole(this IServiceCollection services) =>
17 | services.AddSingleton(AnsiConsole.Console);
18 | }
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Cli/Templates/dapr-component.hbs:
--------------------------------------------------------------------------------
1 | apiVersion: dapr.io/v1alpha1
2 | kind: Component
3 | metadata:
4 | name: {{name}}
5 | labels:
6 | app: {{name}}
7 | spec:
8 | type: {{type}}
9 | version: {{version}}
10 | {{#if emptyMetadata}}
11 | metadata: []
12 | {{/if}}
13 | {{#if hasMetadata}}
14 | metadata:
15 | {{#each metadata}}
16 | - name: {{@key}}
17 | value: {{this}}
18 | {{/each}}
19 | {{/if}}
--------------------------------------------------------------------------------
/src/Aspirate.Cli/Templates/dashboard.hbs:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: aspire-dashboard
5 | labels:
6 | app: aspire-dashboard
7 | spec:
8 | replicas: 1
9 | selector:
10 | matchLabels:
11 | app: aspire-dashboard
12 | template:
13 | metadata:
14 | labels:
15 | app: aspire-dashboard
16 | spec:
17 | {{#if withPrivateRegistry}}
18 | imagePullSecrets:
19 | - name: image-pull-secret
20 | {{/if}}
21 | terminationGracePeriodSeconds: 30
22 | containers:
23 | - name: aspire-dashboard
24 | image: {{containerImage}}
25 | resources:
26 | requests:
27 | cpu: "500m"
28 | memory: "512Mi"
29 | limits:
30 | memory: "512Mi"
31 | ports:
32 | - name: dashboard-ui
33 | containerPort: 18888
34 | - name: otlp
35 | containerPort: 18889
36 | env:
37 | - name: DOTNET_DASHBOARD_UNSECURED_ALLOW_ANONYMOUS
38 | value: "true"
39 | ---
40 | apiVersion: v1
41 | kind: Service
42 | metadata:
43 | name: aspire-dashboard
44 | spec:
45 | selector:
46 | app: aspire-dashboard
47 | ports:
48 | - name: dashboard-ui
49 | protocol: TCP
50 | port: 18888
51 | targetPort: 18888
52 | - name: otlp
53 | protocol: TCP
54 | port: 18889
55 | targetPort: 18889
56 | type: ClusterIP
--------------------------------------------------------------------------------
/src/Aspirate.Cli/Templates/deployment.hbs:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: apps/v1
3 | kind: Deployment
4 | metadata:
5 | name: {{name}}
6 | labels:
7 | app: {{name}}
8 | {{#if hasAnyAnnotations}}
9 | annotations:
10 | {{#each annotations}}
11 | {{@key}}: {{this}}
12 | {{/each}}
13 | {{/if}}
14 | spec:
15 | minReadySeconds: 60
16 | replicas: 1
17 | selector:
18 | matchLabels:
19 | app: {{name}}
20 | strategy:
21 | type: Recreate
22 | template:
23 | metadata:
24 | labels:
25 | app: {{name}}
26 | {{#if hasAnyAnnotations}}
27 | annotations:
28 | {{#each annotations}}
29 | {{@key}}: {{this}}
30 | {{/each}}
31 | {{/if}}
32 | spec:
33 | {{#if withPrivateRegistry}}
34 | imagePullSecrets:
35 | - name: image-pull-secret
36 | {{/if}}
37 | containers:
38 | - name: {{name}}
39 | image: {{containerImage}}
40 | imagePullPolicy: {{imagePullPolicy}}
41 | {{#if entrypoint}}
42 | command:
43 | - {{entrypoint}}
44 | {{/if}}
45 | {{#if hasArgs}}
46 | args:
47 | {{#each args}}
48 | - {{this}}
49 | {{/each}}
50 | {{/if}}
51 | {{#if hasPorts}}
52 | ports:
53 | {{#each ports}}
54 | - name: {{name}}
55 | containerPort: {{internalPort}}
56 | {{/each}}
57 | {{/if}}
58 | {{#if hasAnyEnv}}
59 | envFrom:
60 | - configMapRef:
61 | name: {{name}}-env
62 | {{/if}}
63 | {{#if hasAnySecrets}}
64 | - secretRef:
65 | name: {{name}}-secrets
66 | {{/if}}
67 | terminationGracePeriodSeconds: 180
--------------------------------------------------------------------------------
/src/Aspirate.Cli/Templates/kustomization.hbs:
--------------------------------------------------------------------------------
1 | {{#if withNamespace}}
2 | namespace: {{namespace}}
3 |
4 | {{/if}}
5 | resources:
6 | {{#each manifests}}
7 | - {{this}}
8 | {{/each}}
9 |
10 | generatorOptions:
11 | disableNameSuffixHash: true
12 |
13 | {{#if hasAnyEnv}}
14 | configMapGenerator:
15 | - name: {{name}}-env
16 | literals:
17 | {{#each env}}
18 | - {{@key}}={{this}}
19 | {{/each}}
20 | {{#if isProject}}
21 | - ASPNETCORE_URLS=http://+:8080;
22 | {{/if}}
23 | {{/if}}
24 |
25 | {{#if hasAnySecrets}}
26 | secretGenerator:
27 | - name: {{name}}-secrets
28 | envs:
29 | - .{{name}}.secrets
30 | {{/if}}
--------------------------------------------------------------------------------
/src/Aspirate.Cli/Templates/namespace.hbs:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | name: {{namespace}}
--------------------------------------------------------------------------------
/src/Aspirate.Cli/Templates/service.hbs:
--------------------------------------------------------------------------------
1 | {{#if hasPorts}}
2 | ---
3 | apiVersion: v1
4 | kind: Service
5 | metadata:
6 | name: {{name}}
7 | spec:
8 | type: {{serviceType}}
9 | selector:
10 | app: {{name}}
11 | ports:
12 | {{#each ports}}
13 | - name: {{name}}
14 | {{#if externalPort}}
15 | port: {{externalPort}}
16 | {{else}}
17 | port: {{internalPort}}
18 | {{/if}}
19 | targetPort: {{internalPort}}
20 | {{/each}}
21 | {{/if}}
--------------------------------------------------------------------------------
/src/Aspirate.Cli/nuget-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/prom3theu5/aspirational-manifests/6ad05c192db3926c85b60dc066d0fa7e388751ef/src/Aspirate.Cli/nuget-icon.png
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Actions/BaseAction.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Actions;
2 |
3 | public abstract class BaseAction(IServiceProvider serviceProvider) : IAction
4 | {
5 | protected IAnsiConsole Logger { get; } = serviceProvider.GetRequiredService();
6 | protected AspirateState CurrentState { get; } = serviceProvider.GetRequiredService();
7 | protected IServiceProvider Services { get; } = serviceProvider;
8 |
9 | public abstract Task ExecuteAsync();
10 | protected virtual bool PreviousStateWasRestored(bool withConfirmation = true)
11 | {
12 | if (CurrentState.NonInteractive)
13 | {
14 | return false;
15 | }
16 |
17 | if (!CurrentState.StateWasLoadedFromPrevious)
18 | {
19 | return false;
20 | }
21 |
22 | if (!withConfirmation)
23 | {
24 | return true;
25 | }
26 |
27 | if (CurrentState.UseAllPreviousStateValues)
28 | {
29 | return true;
30 | }
31 |
32 | var shouldSkip = Logger.Confirm("Would you like to skip this action, and use the previous state?");
33 |
34 | if (!shouldSkip)
35 | {
36 | return false;
37 | }
38 |
39 | OnPreviousStateWasRestored();
40 | return true;
41 | }
42 |
43 | protected virtual void OnPreviousStateWasRestored() => Logger.MarkupLine("[bold]Skipping as it was already loaded from previous state.[/]");
44 | }
45 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Actions/BaseActionWithNonInteractiveValidation.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Actions;
2 |
3 | public abstract class BaseActionWithNonInteractiveValidation(IServiceProvider serviceProvider) : BaseAction(serviceProvider)
4 | {
5 | public abstract void ValidateNonInteractiveState();
6 | }
7 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Actions/Configuration/AskImagePullPolicyAction.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Actions.Configuration;
2 |
3 | public class AskImagePullPolicyAction(
4 | IAspirateConfigurationService configurationService,
5 | IServiceProvider serviceProvider) : BaseActionWithNonInteractiveValidation(serviceProvider)
6 | {
7 | public override Task ExecuteAsync()
8 | {
9 | if (PreviousStateWasRestored())
10 | {
11 | return Task.FromResult(true);
12 | }
13 |
14 | Logger.WriteRuler("[purple]Handle Image Pull Policy[/]");
15 |
16 | if (CurrentState.NonInteractive)
17 | {
18 | return Task.FromResult(true);
19 | }
20 |
21 | if (!string.IsNullOrEmpty(CurrentState.ImagePullPolicy))
22 | {
23 | return Task.FromResult(true);
24 | }
25 |
26 | var choices = new List
27 | {
28 | "IfNotPresent",
29 | "Always",
30 | "Never",
31 | };
32 |
33 | var choice = Logger.Prompt(
34 | new SelectionPrompt()
35 | .Title("Select image pull policy for manifests")
36 | .PageSize(10)
37 | .MoreChoicesText("[grey](Move up and down to reveal more choices)[/]")
38 | .AddChoices(choices));
39 |
40 | CurrentState.ImagePullPolicy = choice;
41 |
42 | return Task.FromResult(true);
43 | }
44 |
45 | public override void ValidateNonInteractiveState()
46 | {
47 | if (string.IsNullOrEmpty(CurrentState.ImagePullPolicy))
48 | {
49 | Logger.ValidationFailed("Image pull policy is required when running in non-interactive mode.");
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Actions/IAction.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Actions;
2 |
3 | public interface IAction
4 | {
5 | Task ExecuteAsync();
6 | }
7 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Actions/Manifests/GenerateAspireManifestAction.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Actions.Manifests;
2 |
3 | public sealed class GenerateAspireManifestAction(
4 | IAspireManifestCompositionService manifestCompositionService,
5 | IServiceProvider serviceProvider) : BaseAction(serviceProvider)
6 | {
7 | public override async Task ExecuteAsync()
8 | {
9 | Logger.WriteRuler("[purple]Handling Aspire Manifest[/]");
10 |
11 | if (!string.IsNullOrEmpty(CurrentState.AspireManifest))
12 | {
13 | Logger.MarkupLine($"[bold]Aspire Manifest supplied at path: [blue]{CurrentState.AspireManifest}[/].[/]");
14 | Logger.MarkupLine("[bold]Skipping Aspire Manifest generation.[/]");
15 | return true;
16 | }
17 |
18 | Logger.MarkupLine("[bold]Generating Aspire Manifest for supplied App Host[/]");
19 |
20 | var result = await manifestCompositionService.BuildManifestForProject(CurrentState.ProjectPath, CurrentState.LaunchProfile);
21 |
22 | if (result.Success)
23 | {
24 | CurrentState.AspireManifest = result.FullPath;
25 |
26 | Logger.MarkupLine($"[green]({EmojiLiterals.CheckMark}) Done: [/] Created Aspire Manifest At Path: [blue]{CurrentState.AspireManifest}[/]");
27 |
28 | return true;
29 | }
30 |
31 | Logger.MarkupLine($"[red]Failed to generate Aspire Manifest at: {CurrentState.AspireManifest}[/]");
32 | throw new InvalidOperationException("Failed to generate Aspire Manifest.");
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Actions/Manifests/IncludeAspireDashboardAction.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Actions.Manifests;
2 |
3 | public class IncludeAspireDashboardAction(IServiceProvider serviceProvider) : BaseActionWithNonInteractiveValidation(serviceProvider)
4 | {
5 | public override Task ExecuteAsync()
6 | {
7 | if (PreviousStateWasRestored())
8 | {
9 | return Task.FromResult(true);
10 | }
11 |
12 | Logger.WriteRuler("[purple]Handling Aspire Dashboard[/]");
13 |
14 | if (CurrentState.IncludeDashboard != null)
15 | {
16 | return Task.FromResult(true);
17 | }
18 |
19 | if (CurrentState.NonInteractive)
20 | {
21 | Logger.ValidationFailed("The include dashboard option is required in non-interactive mode.");
22 | }
23 |
24 | AskShouldIncludeDashboard();
25 |
26 | return Task.FromResult(true);
27 | }
28 |
29 | private void AskShouldIncludeDashboard()
30 | {
31 | var shouldIncludeAspireDashboard = Logger.Confirm(
32 | "[bold]Would you like to deploy the aspire dashboard and connect the OTLP endpoint?[/]");
33 |
34 | CurrentState.IncludeDashboard = shouldIncludeAspireDashboard;
35 |
36 | if (!shouldIncludeAspireDashboard)
37 | {
38 | Logger.MarkupLine("[yellow](!)[/] Skipping Aspire Dashboard deployment");
39 | }
40 | }
41 |
42 | public override void ValidateNonInteractiveState()
43 | {
44 | if (CurrentState.IncludeDashboard == null)
45 | {
46 | Logger.ValidationFailed("The include dashboard option is required in non-interactive mode.");
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Actions/Manifests/SubstituteValuesAspireManifestAction.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Actions.Manifests;
2 |
3 | public class SubstituteValuesAspireManifestAction(IServiceProvider serviceProvider, IResourceExpressionProcessor transformer) : BaseAction(serviceProvider)
4 | {
5 | public override Task ExecuteAsync()
6 | {
7 | Logger.WriteRuler("[purple]Handle Value and Parameter Substitution[/]");
8 |
9 | transformer.ProcessEvaluations(CurrentState.LoadedAspireManifestResources);
10 |
11 | return Task.FromResult(true);
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Actions/Secrets/SaveSecretsAction.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Actions.Secrets;
2 |
3 | public class SaveSecretsAction(
4 | IAnsiConsole console,
5 | ISecretService secretService,
6 | IServiceProvider serviceProvider) : BaseAction(serviceProvider)
7 | {
8 | public override Task ExecuteAsync()
9 | {
10 | Logger.WriteRuler("[purple]Populating Secrets File[/]");
11 |
12 | secretService.SaveSecrets(new SecretManagementOptions
13 | {
14 | State = CurrentState,
15 | NonInteractive = CurrentState.NonInteractive,
16 | DisableSecrets = CurrentState.DisableSecrets,
17 | SecretPassword = CurrentState.SecretPassword,
18 | });
19 |
20 | return Task.FromResult(true);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Aspirate.Commands.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Apply/ApplyCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Apply;
2 |
3 | public sealed class ApplyCommand : BaseCommand
4 | {
5 | protected override bool CommandUnlocksSecrets => true;
6 |
7 | public ApplyCommand() : base("apply", "Apply the generated kustomize manifest to the cluster.")
8 | {
9 | AddOption(InputPathOption.Instance);
10 | AddOption(KubernetesContextOption.Instance);
11 | AddOption(SecretPasswordOption.Instance);
12 | AddOption(RollingRestartOption.Instance);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Apply/ApplyCommandHandler.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Apply;
2 |
3 | public sealed class ApplyCommandHandler(IServiceProvider serviceProvider) : BaseCommandOptionsHandler(serviceProvider)
4 | {
5 | public override Task HandleAsync(ApplyOptions optionses) =>
6 | ActionExecutor
7 | .QueueAction(nameof(ApplyManifestsToClusterAction))
8 | .ExecuteCommandsAsync();
9 | }
10 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Apply/ApplyOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Apply;
2 |
3 | public sealed class ApplyOptions : BaseCommandOptions, IKubernetesOptions, IApplyOptions
4 | {
5 | public string? InputPath { get; set; }
6 | public string? KubeContext { get; set; }
7 | public bool? RollingRestart { get; set; }
8 | }
9 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/BaseCommandOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands;
2 |
3 | [ExcludeFromCodeCoverage]
4 | public abstract class BaseCommandOptions : ICommandOptions
5 | {
6 | public bool? NonInteractive { get; set; }
7 | public bool? DisableSecrets { get; set; }
8 | public bool? DisableState { get; set; }
9 | public string? SecretPassword { get; set; }
10 | public string? LaunchProfile { get; set; }
11 | }
12 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/BaseCommandOptionsHandler.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands;
2 |
3 | [ExcludeFromCodeCoverage]
4 | public abstract class BaseCommandOptionsHandler : ICommandOptionsHandler where TOptions : class, ICommandOptions
5 | {
6 | protected BaseCommandOptionsHandler(IServiceProvider serviceProvider)
7 | {
8 | Services = serviceProvider;
9 | CurrentState = Services.GetRequiredService();
10 | ActionExecutor = ActionExecutor.CreateInstance(serviceProvider);
11 | }
12 |
13 | public AspirateState CurrentState { get; set; }
14 | public IServiceProvider Services { get; }
15 | protected ActionExecutor ActionExecutor { get; set; }
16 | public abstract Task HandleAsync(TOptions options);
17 | }
18 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Build/BuildCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Build;
2 |
3 | public sealed class BuildCommand : BaseCommand
4 | {
5 | protected override bool CommandUnlocksSecrets => false;
6 |
7 | public BuildCommand() : base("build", "Builds and pushes containers")
8 | {
9 | AddOption(ProjectPathOption.Instance);
10 | AddOption(AspireManifestOption.Instance);
11 | AddOption(ContainerBuilderOption.Instance);
12 | AddOption(ContainerBuildContextOption.Instance);
13 | AddOption(ContainerImageTagOption.Instance);
14 | AddOption(ContainerBuildArgsOption.Instance);
15 | AddOption(PreferDockerfileOption.Instance);
16 | AddOption(ContainerRegistryOption.Instance);
17 | AddOption(ContainerRepositoryPrefixOption.Instance);
18 | AddOption(RuntimeIdentifierOption.Instance);
19 | AddOption(ComponentsOption.Instance);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Build/BuildCommandHandler.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Build;
2 |
3 | public sealed class BuildCommandHandler(IServiceProvider serviceProvider) : BaseCommandOptionsHandler(serviceProvider)
4 | {
5 | public override Task HandleAsync(BuildOptions options) =>
6 | ActionExecutor
7 | .QueueAction(nameof(LoadConfigurationAction))
8 | .QueueAction(nameof(GenerateAspireManifestAction))
9 | .QueueAction(nameof(LoadAspireManifestAction))
10 | .QueueAction(nameof(PopulateContainerDetailsForProjectsAction))
11 | .QueueAction(nameof(BuildAndPushContainersFromProjectsAction))
12 | .QueueAction(nameof(BuildAndPushContainersFromDockerfilesAction))
13 | .ExecuteCommandsAsync();
14 | }
15 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Build/BuildOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Build;
2 |
3 | public sealed class BuildOptions : BaseCommandOptions, IBuildOptions, IContainerOptions, IAspireOptions, IComponentsOptions
4 | {
5 | public string? ProjectPath { get; set; }
6 | public string? AspireManifest { get; set; }
7 | public string? ContainerBuilder { get; set; }
8 | public string? ContainerBuildContext { get; set; }
9 | public string? ContainerRegistry { get; set; }
10 | public List? ContainerBuildArgs { get; set; }
11 | public string? ContainerRepositoryPrefix { get; set; }
12 | public List? ContainerImageTags { get; set; }
13 | public string? RuntimeIdentifier { get; set; }
14 | public List? ComposeBuilds { get; set; }
15 | public bool PreferDockerfile { get; set; }
16 | public List? CliSpecifiedComponents { get; set; }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Destroy/DestroyCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Destroy;
2 |
3 | public sealed class DestroyCommand : BaseCommand
4 | {
5 | protected override bool CommandUnlocksSecrets => false;
6 |
7 | public DestroyCommand() : base("destroy", "Removes the manifests from your cluster..")
8 | {
9 | AddOption(InputPathOption.Instance);
10 | AddOption(KubernetesContextOption.Instance);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Destroy/DestroyCommandHandler.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Destroy;
2 |
3 | public sealed class DestroyCommandHandler(IServiceProvider serviceProvider) : BaseCommandOptionsHandler(serviceProvider)
4 | {
5 | public override Task HandleAsync(DestroyOptions options) =>
6 | ActionExecutor
7 | .QueueAction(nameof(RemoveManifestsFromClusterAction))
8 | .ExecuteCommandsAsync();
9 | }
10 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Destroy/DestroyOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Destroy;
2 |
3 | public sealed class DestroyOptions : BaseCommandOptions, IKubernetesOptions
4 | {
5 | public string? InputPath { get; set; }
6 | public string? KubeContext { get; set; }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Generate/GenerateOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Generate;
2 |
3 | public sealed class GenerateOptions : BaseCommandOptions,
4 | IBuildOptions,
5 | IContainerOptions,
6 | IAspireOptions,
7 | IGenerateOptions,
8 | IPrivateRegistryCredentialsOptions,
9 | IDashboardOptions,
10 | ISecretState,
11 | IComponentsOptions
12 | {
13 | public string? ProjectPath { get; set; }
14 | public string? AspireManifest { get; set; }
15 | public string? OutputPath { get; set; }
16 | public string? Namespace { get; set; }
17 | public bool? SkipBuild { get; set; }
18 | public bool? SkipFinalKustomizeGeneration { get; set; }
19 | public string? ContainerBuilder { get; set; }
20 | public List? ContainerBuildArgs { get; set; }
21 | public string? ContainerBuildContext { get; set; }
22 | public string? ContainerRegistry { get; set; }
23 | public string? ContainerRepositoryPrefix { get; set; }
24 | public List? ContainerImageTags { get; set; }
25 | public string? ImagePullPolicy { get; set; }
26 | public string? OutputFormat { get; set; }
27 | public List? Parameters { get; set; }
28 | public string? RuntimeIdentifier { get; set; }
29 | public List? ComposeBuilds { get; set; }
30 | public bool PreferDockerfile { get; set; }
31 | public string? PrivateRegistryUrl { get; set; }
32 | public string? PrivateRegistryUsername { get; set; }
33 | public string? PrivateRegistryPassword { get; set; }
34 | public string? PrivateRegistryEmail { get; set; }
35 | public bool? WithPrivateRegistry { get; set; }
36 | public bool? IncludeDashboard { get; set; }
37 | public bool? ReplaceSecrets { get; set; }
38 | public List? CliSpecifiedComponents { get; set; }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/GenericCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands;
2 |
3 | [ExcludeFromCodeCoverage]
4 | public class GenericCommand : Command
5 | {
6 | public GenericCommand(string name, string description)
7 | : base(name, description) =>
8 | Handler = CommandHandler.Create(ExecuteCommand);
9 |
10 | protected virtual Task ExecuteCommand(IServiceCollection services) => Task.FromResult(0);
11 |
12 | protected static Table CreateHelpTable()
13 | {
14 | var table = new Table();
15 | table.AddColumn("Sub Commands");
16 | table.AddColumn("Description");
17 | return table;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Init/InitCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Init;
2 |
3 | public sealed class InitCommand : BaseCommand
4 | {
5 | protected override bool CommandUnlocksSecrets => false;
6 | protected override bool CommandSkipsStateAndSecrets => true;
7 |
8 | public InitCommand() : base("init", "Initializes aspirate settings within your AppHost directory.")
9 | {
10 | AddOption(ProjectPathOption.Instance);
11 | AddOption(ContainerBuilderOption.Instance);
12 | AddOption(ContainerBuildArgsOption.Instance);
13 | AddOption(ContainerBuildContextOption.Instance);
14 | AddOption(ContainerRegistryOption.Instance);
15 | AddOption(ContainerRepositoryPrefixOption.Instance);
16 | AddOption(ContainerImageTagOption.Instance);
17 | AddOption(TemplatePathOption.Instance);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Init/InitCommandHandler.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Init;
2 |
3 | public class InitCommandHandler(IServiceProvider serviceProvider) : BaseCommandOptionsHandler(serviceProvider)
4 | {
5 | public override Task HandleAsync(InitOptions options) =>
6 | ActionExecutor
7 | .QueueAction(nameof(InitializeConfigurationAction))
8 | .ExecuteCommandsAsync();
9 | }
10 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Init/InitOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Init;
2 |
3 | public sealed class InitOptions : BaseCommandOptions, IInitOptions, IContainerOptions
4 | {
5 | public string? ProjectPath { get; set; }
6 |
7 | public string? ContainerBuilder { get; set; }
8 | public string? ContainerBuildContext { get; set; }
9 |
10 | public string? ContainerRegistry { get; set; }
11 |
12 | public string? ContainerRepositoryPrefix { get; set; }
13 |
14 | public List? ContainerImageTags { get; set; }
15 | public List? ContainerBuildArgs { get; set; }
16 | public string? TemplatePath { get; set; }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Run/RunCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Run;
2 |
3 | public sealed class RunCommand : BaseCommand
4 | {
5 | protected override bool CommandUnlocksSecrets => true;
6 |
7 | public RunCommand() : base("run", "Builds, pushes containers, and runs the current solution directly against a kubernetes cluster.")
8 | {
9 | AddOption(ProjectPathOption.Instance);
10 | AddOption(AspireManifestOption.Instance);
11 | AddOption(SkipBuildOption.Instance);
12 | AddOption(ContainerBuilderOption.Instance);
13 | AddOption(ContainerBuildContextOption.Instance);
14 | AddOption(ContainerImageTagOption.Instance);
15 | AddOption(ContainerBuildArgsOption.Instance);
16 | AddOption(PreferDockerfileOption.Instance);
17 | AddOption(ContainerRegistryOption.Instance);
18 | AddOption(ContainerRepositoryPrefixOption.Instance);
19 | AddOption(ImagePullPolicyOption.Instance);
20 | AddOption(NamespaceOption.Instance);
21 | AddOption(RuntimeIdentifierOption.Instance);
22 | AddOption(SecretPasswordOption.Instance);
23 | AddOption(PrivateRegistryOption.Instance);
24 | AddOption(PrivateRegistryUrlOption.Instance);
25 | AddOption(PrivateRegistryUsernameOption.Instance);
26 | AddOption(PrivateRegistryPasswordOption.Instance);
27 | AddOption(PrivateRegistryEmailOption.Instance);
28 | AddOption(IncludeDashboardOption.Instance);
29 | AddOption(AllowClearNamespaceOption.Instance);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Run/RunCommandHandler.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Run;
2 |
3 | public sealed class RunCommandHandler(IServiceProvider serviceProvider) : BaseCommandOptionsHandler(serviceProvider)
4 | {
5 | public override Task HandleAsync(RunOptions options) =>
6 | ActionExecutor
7 | .QueueAction(nameof(LoadConfigurationAction))
8 | .QueueAction(nameof(GenerateAspireManifestAction))
9 | .QueueAction(nameof(LoadAspireManifestAction))
10 | .QueueAction(nameof(IncludeAspireDashboardAction))
11 | .QueueAction(nameof(PopulateInputsAction))
12 | .QueueAction(nameof(SubstituteValuesAspireManifestAction))
13 | .QueueAction(nameof(ApplyDaprAnnotationsAction))
14 | .QueueAction(nameof(PopulateContainerDetailsForProjectsAction))
15 | .QueueAction(nameof(BuildAndPushContainersFromProjectsAction))
16 | .QueueAction(nameof(BuildAndPushContainersFromDockerfilesAction))
17 | .QueueAction(nameof(AskImagePullPolicyAction))
18 | .QueueAction(nameof(SaveSecretsAction))
19 | .QueueAction(nameof(CustomNamespaceAction))
20 | .QueueAction(nameof(RunKubernetesObjectsAction))
21 | .ExecuteCommandsAsync();
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Run/RunOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Run;
2 |
3 | public sealed class RunOptions : BaseCommandOptions,
4 | IContainerOptions,
5 | IAspireOptions,
6 | IPrivateRegistryCredentialsOptions,
7 | IDashboardOptions,
8 | IRunOptions
9 | {
10 | public string? ProjectPath { get; set; }
11 | public string? AspireManifest { get; set; }
12 | public bool? AllowClearNamespace { get; set; }
13 | public string? Namespace { get; set; }
14 | public bool? SkipBuild { get; set; }
15 | public string? ContainerBuilder { get; set; }
16 | public List? ContainerBuildArgs { get; set; }
17 | public string? ContainerBuildContext { get; set; }
18 | public string? ContainerRegistry { get; set; }
19 | public string? ContainerRepositoryPrefix { get; set; }
20 | public List? ContainerImageTags { get; set; }
21 | public string? ImagePullPolicy { get; set; }
22 | public string? RuntimeIdentifier { get; set; }
23 | public string? PrivateRegistryUrl { get; set; }
24 | public string? PrivateRegistryUsername { get; set; }
25 | public string? PrivateRegistryPassword { get; set; }
26 | public string? PrivateRegistryEmail { get; set; }
27 | public bool? WithPrivateRegistry { get; set; }
28 | public bool? IncludeDashboard { get; set; }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Stop/StopCommand.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Stop;
2 |
3 | public sealed class StopCommand() : BaseCommand("stop", "Stops a deployment that has been made using 'aspirate run' by destroying it.")
4 | {
5 | protected override bool CommandUnlocksSecrets => false;
6 | protected override bool CommandAlwaysRequiresState => true;
7 | }
8 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Stop/StopCommandHandler.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Stop;
2 |
3 | public sealed class StopCommandHandler(IServiceProvider serviceProvider) : BaseCommandOptionsHandler(serviceProvider)
4 | {
5 | public override Task HandleAsync(StopOptions options) =>
6 | ActionExecutor
7 | .QueueAction(nameof(StopDeployedKubernetesInstanceAction))
8 | .ExecuteCommandsAsync();
9 | }
10 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Commands/Stop/StopOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Commands.Stop;
2 |
3 | public sealed class StopOptions : BaseCommandOptions;
4 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/AllowClearNamespaceOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class AllowClearNamespaceOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--clear-namespace"];
6 |
7 | private AllowClearNamespaceOption() : base(_aliases, "ASPIRATE_ALLOW_CLEAR_NAMESPACE", null)
8 | {
9 | Name = nameof(IRunOptions.AllowClearNamespace);
10 | Description = "Is Aspirate allowed to clear the namespace if it exists before deploying during the run command?";
11 | Arity = ArgumentArity.ZeroOrOne;
12 | IsRequired = false;
13 | }
14 |
15 | public static AllowClearNamespaceOption Instance { get; } = new();
16 |
17 | public override bool IsSecret => false;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/AspireManifestOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class AspireManifestOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-m",
8 | "--aspire-manifest"
9 | ];
10 |
11 | private AspireManifestOption() : base(_aliases, "ASPIRATE_ASPIRE_MANIFEST_PATH", null)
12 | {
13 | Name = nameof(IAspireOptions.AspireManifest);
14 | Description ="The aspire manifest file to use";
15 | Arity = ArgumentArity.ExactlyOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static AspireManifestOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/BaseOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public abstract class BaseOption(
4 | string[] aliases,
5 | string envName,
6 | T defaultValue) :
7 | Option(
8 | aliases,
9 | getDefaultValue: GetOptionDefault(envName, defaultValue)),
10 | IBaseOption
11 | {
12 | public abstract bool IsSecret { get; }
13 |
14 | public T GetOptionDefault() => GetOptionDefault(envName, defaultValue)();
15 |
16 | object? IBaseOption.GetOptionDefault() => GetOptionDefault();
17 |
18 | private static Func GetOptionDefault(string envVarName, TReturnValue defaultValue) =>
19 | () =>
20 | {
21 | var envValue = Environment.GetEnvironmentVariable(envVarName);
22 | if (envValue == null)
23 | {
24 | return defaultValue;
25 | }
26 |
27 | try
28 | {
29 | return (TReturnValue)Convert.ChangeType(envValue, typeof(TReturnValue));
30 | }
31 | catch (Exception)
32 | {
33 | return defaultValue;
34 | }
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ComponentsOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ComponentsOption : BaseOption?>
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-c",
8 | "--components"
9 | ];
10 |
11 | private ComponentsOption() : base(_aliases, "ASPIRATE_COMPONENTS", null)
12 | {
13 | Name = nameof(IComponentsOptions.CliSpecifiedComponents);
14 | Description = "Specify which components build or generate, non interactively";
15 | Arity = ArgumentArity.ZeroOrMore;
16 | IsRequired = false;
17 | }
18 |
19 | public static ComponentsOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ComposeBuildsOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ComposeBuildsOption : BaseOption?>
4 | {
5 | private static readonly string[] _aliases = ["--compose-build"];
6 |
7 | private ComposeBuildsOption() : base(_aliases, "ASPIRATE_COMPOSE_BUILDS", null)
8 | {
9 | Name = nameof(IBuildOptions.ComposeBuilds);
10 | Description = "Specify the resource names which will be built by the compose file.";
11 | Arity = ArgumentArity.ZeroOrMore;
12 | IsRequired = false;
13 | }
14 |
15 | public static ComposeBuildsOption Instance { get; } = new();
16 |
17 | public override bool IsSecret => false;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ContainerBuildArgsOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ContainerBuildArgsOption : BaseOption?>
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-cba",
8 | "--container-build-arg"
9 | ];
10 |
11 | private ContainerBuildArgsOption() : base(_aliases, "ASPIRATE_CONTAINER_BUILD_ARGS", null)
12 | {
13 | Name = nameof(IContainerOptions.ContainerBuildArgs);
14 | Description = "The Container Build Arguments to use for all containers. In \"key\"=\"value\" format. Can include multiple times.";
15 | Arity = ArgumentArity.ZeroOrMore;
16 | IsRequired = false;
17 | }
18 |
19 | public static ContainerBuildArgsOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ContainerBuildContextOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ContainerBuildContextOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-cbc",
8 | "--container-build-context"
9 | ];
10 |
11 | private ContainerBuildContextOption() : base(_aliases, "ASPIRATE_CONTAINER_BUILD_CONTEXT", null)
12 | {
13 | Name = nameof(IContainerOptions.ContainerBuildContext);
14 | Description = "The Container Build Context to use when Dockerfile is used to build projects";
15 | Arity = ArgumentArity.ExactlyOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static ContainerBuildContextOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ContainerBuilderOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ContainerBuilderOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--container-builder"];
6 |
7 | private ContainerBuilderOption() : base(_aliases, "ASPIRATE_CONTAINER_BUILDER", "docker")
8 | {
9 | Name = nameof(IContainerOptions.ContainerBuilder);
10 | Description = "The Container Builder: can be 'docker' or 'podman'. The default is 'docker'";
11 | Arity = ArgumentArity.ExactlyOne;
12 | IsRequired = false;
13 | AddValidator(ValidateFormat);
14 | }
15 |
16 | public static ContainerBuilderOption Instance { get; } = new();
17 |
18 | public override bool IsSecret => false;
19 |
20 | private static void ValidateFormat(OptionResult optionResult)
21 | {
22 | var value = optionResult.GetValueOrDefault();
23 |
24 | if (value is null)
25 | {
26 | throw new ArgumentException("--container-builder cannot be null.");
27 | }
28 |
29 | if (!ContainerBuilder.TryFromValue(value.ToLower(), out _))
30 | {
31 | var errorBuilder = new StringBuilder();
32 | errorBuilder.Append("--container-builder must be one of: '");
33 | errorBuilder.AppendJoin("', '", ContainerBuilder.List.Select(x => x.Value));
34 | errorBuilder.Append("' and not quoted.");
35 |
36 | throw new ArgumentException(errorBuilder.ToString());
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ContainerImageTagOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ContainerImageTagOption : BaseOption?>
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-ct",
8 | "--container-image-tag"
9 | ];
10 |
11 | private ContainerImageTagOption() : base(_aliases, "ASPIRATE_CONTAINER_IMAGE_TAG", null)
12 | {
13 | Name = nameof(IContainerOptions.ContainerImageTags);
14 | Description = "The Container Image Tags to use for all containers. Can include multiple times.";
15 | Arity = ArgumentArity.ZeroOrMore;
16 | IsRequired = false;
17 | }
18 |
19 | public static ContainerImageTagOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ContainerRegistryOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ContainerRegistryOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-cr",
8 | "--container-registry"
9 | ];
10 |
11 | private ContainerRegistryOption() : base(_aliases, "ASPIRATE_CONTAINER_REGISTRY", null)
12 | {
13 | Name = nameof(IContainerOptions.ContainerRegistry);
14 | Description = "The Container Registry to use as the fall-back value for all containers";
15 | Arity = ArgumentArity.ExactlyOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static ContainerRegistryOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ContainerRepositoryPrefixOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ContainerRepositoryPrefixOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "--container-repository-prefix",
8 | "-crp"
9 | ];
10 |
11 | private ContainerRepositoryPrefixOption() : base(_aliases, "ASPIRATE_CONTAINER_REPOSITORY_PREFIX", null)
12 | {
13 | Name = nameof(IContainerOptions.ContainerRepositoryPrefix);
14 | Description = "The Container repository prefix to use as the fall-back value for all containers";
15 | Arity = ArgumentArity.ExactlyOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static ContainerRepositoryPrefixOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/DisableSecretsOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class DisableSecretsOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--disable-secrets"];
6 |
7 | private DisableSecretsOption() : base(_aliases, "ASPIRATE_DISABLE_SECRETS", null)
8 | {
9 | Name = nameof(ICommandOptions.DisableSecrets);
10 | Description = "Disables Secret Support";
11 | Arity = ArgumentArity.ZeroOrOne;
12 | IsRequired = false;
13 | }
14 |
15 | public static DisableSecretsOption Instance { get; } = new();
16 |
17 | public override bool IsSecret => false;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/DisableStateOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class DisableStateOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--disable-state", "--no-state"];
6 |
7 | private DisableStateOption() : base(_aliases, "ASPIRATE_DISABLE_STATE", null)
8 | {
9 | Name = nameof(ICommandOptions.DisableState);
10 | Description = "Disables State Support";
11 | Arity = ArgumentArity.ZeroOrOne;
12 | IsRequired = false;
13 | }
14 |
15 | public static DisableStateOption Instance { get; } = new();
16 |
17 | public override bool IsSecret => false;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/IBaseOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public interface IBaseOption
4 | {
5 | bool IsSecret { get; }
6 |
7 | public object? GetOptionDefault();
8 | }
9 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ImagePullPolicyOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ImagePullPolicyOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--image-pull-policy"];
6 |
7 | private ImagePullPolicyOption() : base(_aliases, "ASPIRATE_IMAGE_PULL_POLICY", null)
8 | {
9 | Name = nameof(IGenerateOptions.ImagePullPolicy);
10 | Description = "The Image pull policy to use when generating manifests";
11 | Arity = ArgumentArity.ExactlyOne;
12 | IsRequired = false;
13 | AddValidator(ValidateFormat);
14 | }
15 |
16 | public static ImagePullPolicyOption Instance { get; } = new();
17 |
18 | public override bool IsSecret => false;
19 |
20 | private static void ValidateFormat(OptionResult optionResult)
21 | {
22 | var value = optionResult.GetValueOrDefault();
23 |
24 | if (value is null)
25 | {
26 | return;
27 | }
28 |
29 | if (!ImagePullPolicy.TryFromValue(value, out _))
30 | {
31 | var errorBuilder = new StringBuilder();
32 | errorBuilder.Append("--image-pull-policy must be one of: '");
33 | errorBuilder.AppendJoin("', '", ImagePullPolicy.List.Select(x => x.Value));
34 | errorBuilder.Append("' and not quoted. It is case sensitive.");
35 |
36 | throw new ArgumentException(errorBuilder.ToString());
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/IncludeDashboardOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class IncludeDashboardOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--include-dashboard", "--with-dashboard"];
6 |
7 | private IncludeDashboardOption() : base(_aliases, "ASPIRATE_INCLUDE_DASHBOARD", null)
8 | {
9 | Name = nameof(IDashboardOptions.IncludeDashboard);
10 | Description = "Include the Aspire Dashboard in the generated manifests";
11 | Arity = ArgumentArity.ZeroOrOne;
12 | IsRequired = false;
13 | }
14 |
15 | public static IncludeDashboardOption Instance { get; } = new();
16 |
17 | public override bool IsSecret => false;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/InputPathOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class InputPathOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-i",
8 | "--input-path"
9 | ];
10 |
11 | private InputPathOption() : base(_aliases, "ASPIRATE_INPUT_PATH", AspirateLiterals.DefaultArtifactsPath)
12 | {
13 | Name = nameof(IKubernetesOptions.InputPath);
14 | Description = "The path for the kustomize manifests directory";
15 | Arity = ArgumentArity.ExactlyOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static InputPathOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/KubernetesContextOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class KubernetesContextOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-k",
8 | "--kube-context"
9 | ];
10 |
11 | private KubernetesContextOption() : base(_aliases, "ASPIRATE_KUBERNETES_CONTEXT", null)
12 | {
13 | Name = nameof(IKubernetesOptions.KubeContext);
14 | Description = "The name of the kubernetes context to use";
15 | Arity = ArgumentArity.ExactlyOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static KubernetesContextOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/LaunchProfileOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class LaunchProfileOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-lp",
8 | "--launch-profile"
9 | ];
10 |
11 | private LaunchProfileOption() : base(_aliases, "ASPIRATE_LAUNCH_PROFILE", null)
12 | {
13 | Name = nameof(ICommandOptions.LaunchProfile);
14 | Description = "The launch profile to use when building the aspire manifest from the AppHost.";
15 | Arity = ArgumentArity.ExactlyOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static LaunchProfileOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/NamespaceOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class NamespaceOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "--namespace"
8 | ];
9 |
10 | private NamespaceOption() : base(_aliases, "ASPIRATE_NAMESPACE", null)
11 | {
12 | Name = nameof(IGenerateOptions.Namespace);
13 | Description = "The Namespace to use for deployments";
14 | Arity = ArgumentArity.ExactlyOne;
15 | IsRequired = false;
16 | }
17 |
18 | public static NamespaceOption Instance { get; } = new();
19 |
20 | public override bool IsSecret => false;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/NonInteractiveOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class NonInteractiveOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "--non-interactive"
8 | ];
9 |
10 | private NonInteractiveOption() : base(_aliases, "ASPIRATE_NON_INTERACTIVE", null)
11 | {
12 | Name = nameof(ICommandOptions.NonInteractive);
13 | Description = "Disables interactive mode for the command";
14 | Arity = ArgumentArity.ZeroOrOne;
15 | IsRequired = false;
16 | }
17 |
18 | public static NonInteractiveOption Instance { get; } = new();
19 |
20 | public override bool IsSecret => false;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/OutputFormatOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class OutputFormatOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--output-format"];
6 |
7 | private OutputFormatOption() : base(_aliases, "ASPIRATE_OUTPUT_FORMAT", null)
8 | {
9 | Name = nameof(IGenerateOptions.OutputFormat);
10 | Description = "The output format of the generated manifests. Supported values are 'kustomize', 'compose', and 'helm'.";
11 | Arity = ArgumentArity.ZeroOrOne;
12 | IsRequired = false;
13 | AddValidator(ValidateFormat);
14 | }
15 |
16 | public static OutputFormatOption Instance { get; } = new();
17 |
18 | public override bool IsSecret => false;
19 |
20 | private static void ValidateFormat(OptionResult optionResult)
21 | {
22 | var value = optionResult.GetValueOrDefault();
23 |
24 | if (value is null)
25 | {
26 | return;
27 | }
28 |
29 | if (!OutputFormat.TryFromValue(value, out _))
30 | {
31 | var errorBuilder = new StringBuilder();
32 | errorBuilder.Append("--output-format must be one of: '");
33 | errorBuilder.AppendJoin("', '", OutputFormat.List.Select(x => x.Value));
34 | errorBuilder.Append("' and not quoted.");
35 |
36 | throw new ArgumentException(errorBuilder.ToString());
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/OutputPathOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class OutputPathOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-o",
8 | "--output-path"
9 | ];
10 |
11 | private OutputPathOption() : base(_aliases, "ASPIRATE_OUTPUT_PATH", AspirateLiterals.DefaultArtifactsPath)
12 | {
13 | Name = nameof(IGenerateOptions.OutputPath);
14 | Description = "The output path for generated manifests";
15 | Arity = ArgumentArity.ExactlyOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static OutputPathOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ParameterResourceValueOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ParameterResourceValueOption : BaseOption?>
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-pa",
8 | "--parameter"
9 | ];
10 |
11 | private ParameterResourceValueOption() : base(_aliases, "ASPIRATE_PARAMETER_VALUE", null)
12 | {
13 | Name = nameof(IGenerateOptions.Parameters);
14 | Description = "The parameter resource value.";
15 | Arity = ArgumentArity.ZeroOrMore;
16 | IsRequired = false;
17 | }
18 |
19 | public static ParameterResourceValueOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/PreferDockerfileOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class PreferDockerfileOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--prefer-dockerfile"];
6 |
7 | private PreferDockerfileOption() : base(_aliases, "ASPIRATE_PREFER_DOCKERFILE", null)
8 | {
9 | Name = nameof(IBuildOptions.PreferDockerfile);
10 | Description = "Instructs to use Dockerfile when available to build project images";
11 | Arity = ArgumentArity.ZeroOrOne;
12 | IsRequired = false;
13 | }
14 |
15 | public static PreferDockerfileOption Instance { get; } = new();
16 |
17 | public override bool IsSecret => false;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/PrivateRegistryEmailOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class PrivateRegistryEmailOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "--private-registry-email"
8 | ];
9 |
10 | private PrivateRegistryEmailOption() : base(_aliases, "ASPIRATE_PRIVATE_REGISTRY_EMAIL", "aspir8@aka.ms")
11 | {
12 | Name = nameof(IPrivateRegistryCredentialsOptions.PrivateRegistryEmail);
13 | Description = "The Private Registry email. It is required and defaults to 'aspirate@aspirate.com'.";
14 | Arity = ArgumentArity.ExactlyOne;
15 | IsRequired = false;
16 | }
17 |
18 | public static PrivateRegistryEmailOption Instance { get; } = new();
19 |
20 | public override bool IsSecret => false;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/PrivateRegistryOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class PrivateRegistryOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "--private-registry"
8 | ];
9 |
10 | private PrivateRegistryOption() : base(_aliases, "ASPIRATE_PRIVATE_REGISTRY", null)
11 | {
12 | Name = nameof(IPrivateRegistryCredentialsOptions.WithPrivateRegistry);
13 | Description = "Enables Private registry imagePullSecret. You will need to supply username and password as well.";
14 | Arity = ArgumentArity.ZeroOrOne;
15 | IsRequired = false;
16 | }
17 |
18 | public static PrivateRegistryOption Instance { get; } = new();
19 |
20 | public override bool IsSecret => false;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/PrivateRegistryPasswordOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class PrivateRegistryPasswordOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "--private-registry-password"
8 | ];
9 |
10 | private PrivateRegistryPasswordOption() : base(_aliases, "ASPIRATE_PRIVATE_REGISTRY_PASSWORD", null)
11 | {
12 | Name = nameof(IPrivateRegistryCredentialsOptions.PrivateRegistryPassword);
13 | Description = "The Private Registry password.";
14 | Arity = ArgumentArity.ExactlyOne;
15 | IsRequired = false;
16 | }
17 |
18 | public static PrivateRegistryPasswordOption Instance { get; } = new();
19 |
20 | public override bool IsSecret => true;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/PrivateRegistryUrlOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class PrivateRegistryUrlOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "--private-registry-url"
8 | ];
9 |
10 | private PrivateRegistryUrlOption() : base(_aliases, "ASPIRATE_PRIVATE_REGISTRY_URL", null)
11 | {
12 | Name = nameof(IPrivateRegistryCredentialsOptions.PrivateRegistryUrl);
13 | Description = "The Private Registry url.";
14 | Arity = ArgumentArity.ExactlyOne;
15 | IsRequired = false;
16 | }
17 |
18 | public static PrivateRegistryUrlOption Instance { get; } = new();
19 |
20 | public override bool IsSecret => false;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/PrivateRegistryUsernameOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class PrivateRegistryUsernameOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "--private-registry-username"
8 | ];
9 |
10 | private PrivateRegistryUsernameOption() : base(_aliases, "ASPIRATE_PRIVATE_REGISTRY_USERNAME", null)
11 | {
12 | Name = nameof(IPrivateRegistryCredentialsOptions.PrivateRegistryUsername);
13 | Description = "The Private Registry username.";
14 | Arity = ArgumentArity.ExactlyOne;
15 | IsRequired = false;
16 | }
17 |
18 | public static PrivateRegistryUsernameOption Instance { get; } = new();
19 |
20 | public override bool IsSecret => false;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ProjectPathOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ProjectPathOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-p",
8 | "--project-path"
9 | ];
10 |
11 | private ProjectPathOption() : base(_aliases, "ASPIRATE_PROJECT_PATH", AspirateLiterals.DefaultAspireProjectPath)
12 | {
13 | Name = nameof(IAspireOptions.ProjectPath);
14 | Description = "The path to the aspire project";
15 | Arity = ArgumentArity.ExactlyOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static ProjectPathOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/ReplaceSecretsOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class ReplaceSecretsOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--replace-secrets"];
6 |
7 | private ReplaceSecretsOption() : base(_aliases, "ASPIRATE_REPLACE_SECRETS", null)
8 | {
9 | Name = nameof(ISecretState.ReplaceSecrets);
10 | Description = "Replace all secrets and inputs.";
11 | Arity = ArgumentArity.ZeroOrOne;
12 | IsRequired = false;
13 | }
14 |
15 | public static ReplaceSecretsOption Instance { get; } = new();
16 |
17 | public override bool IsSecret => false;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/RollingRestartOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class RollingRestartOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-r",
8 | "--rolling-restart"
9 | ];
10 |
11 | private RollingRestartOption() : base(_aliases, "ASPIRATE_ROLLING_RESTART", null)
12 | {
13 | Name = nameof(IApplyOptions.RollingRestart);
14 | Description = "Indicates if a rolling restart should occur at the end of deploy. Defaults to 'false'.";
15 | Arity = ArgumentArity.ZeroOrOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static RollingRestartOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/RuntimeIdentifierOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class RuntimeIdentifierOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--runtime-identifier"];
6 |
7 | private RuntimeIdentifierOption() : base(_aliases, "ASPIRATE_RUNTIME_IDENTIFIER", null)
8 | {
9 | Name = nameof(IBuildOptions.RuntimeIdentifier);
10 | Description = "The Custom Runtime identifier to use for .net project builds.";
11 | Arity = ArgumentArity.ExactlyOne;
12 | IsRequired = false;
13 | }
14 |
15 | public static RuntimeIdentifierOption Instance { get; } = new();
16 |
17 | public override bool IsSecret => false;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/SecretPasswordOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class SecretPasswordOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "--secret-password"
8 | ];
9 |
10 | private SecretPasswordOption() : base(_aliases, "ASPIRATE_SECRET_PASSWORD", null)
11 | {
12 | Name = nameof(ICommandOptions.SecretPassword);
13 | Description = "The Secret Password to use";
14 | Arity = ArgumentArity.ExactlyOne;
15 | IsRequired = false;
16 | }
17 |
18 | public static SecretPasswordOption Instance { get; } = new();
19 |
20 | public override bool IsSecret => true;
21 | }
22 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/SkipBuildOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class SkipBuildOption : BaseOption
4 | {
5 | private static readonly string[] _aliases = ["--skip-build"];
6 |
7 | private SkipBuildOption() : base(_aliases, "ASPIRATE_SKIP_BUILD", null)
8 | {
9 | Name = nameof(IGenerateOptions.SkipBuild);
10 | Description = "Skips build and Push of containers";
11 | Arity = ArgumentArity.ZeroOrOne;
12 | IsRequired = false;
13 | }
14 |
15 | public static SkipBuildOption Instance { get; } = new();
16 |
17 | public override bool IsSecret => false;
18 | }
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/SkipFinalKustomizeGenerationOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class SkipFinalKustomizeGenerationOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-sf",
8 | "--skip-final",
9 | "--skip-final-kustomize-generation"
10 | ];
11 |
12 | private SkipFinalKustomizeGenerationOption() : base(_aliases, "ASPIRATE_SKIP_FINAL_KUSTOMIZE_GENERATION", null)
13 | {
14 | Name = nameof(IGenerateOptions.SkipFinalKustomizeGeneration);
15 | Description = "Skips The final generation of the kustomize manifest, which is the parent top level file";
16 | Arity = ArgumentArity.ZeroOrOne;
17 | IsRequired = false;
18 | }
19 |
20 | public static SkipFinalKustomizeGenerationOption Instance { get; } = new();
21 |
22 | public override bool IsSecret => false;
23 | }
24 |
--------------------------------------------------------------------------------
/src/Aspirate.Commands/Options/TemplatePathOption.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Commands.Options;
2 |
3 | public sealed class TemplatePathOption : BaseOption
4 | {
5 | private static readonly string[] _aliases =
6 | [
7 | "-tp",
8 | "--template-path"
9 | ];
10 |
11 | private TemplatePathOption() : base(_aliases, "ASPIRATE_TEMPLATE_PATH", null)
12 | {
13 | Name = nameof(IInitOptions.TemplatePath);
14 | Description = "The Custom Template path to use";
15 | Arity = ArgumentArity.ExactlyOne;
16 | IsRequired = false;
17 | }
18 |
19 | public static TemplatePathOption Instance { get; } = new();
20 |
21 | public override bool IsSecret => false;
22 | }
23 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Aspirate.Processors.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Resources/AbstractProcessors/ContainerProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Resources.AbstractProcessors;
2 |
3 | ///
4 | /// A container component for version 0 of Aspire.
5 | ///
6 | public class ContainerProcessor(
7 | IFileSystem fileSystem,
8 | IAnsiConsole console,
9 | ISecretProvider secretProvider,
10 | IContainerCompositionService containerCompositionService,
11 | IContainerDetailsService containerDetailsService,
12 | IManifestWriter manifestWriter)
13 | : BaseContainerProcessor(
14 | fileSystem,
15 | console,
16 | secretProvider,
17 | containerCompositionService,
18 | containerDetailsService,
19 | manifestWriter)
20 | {
21 | ///
22 | public override string ResourceType => AspireComponentLiterals.Container;
23 | }
24 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Resources/AbstractProcessors/ContainerV1Processor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Resources.AbstractProcessors;
2 |
3 | ///
4 | /// A container component for version 1 of Aspire.
5 | ///
6 | public class ContainerV1Processor(
7 | IFileSystem fileSystem,
8 | IAnsiConsole console,
9 | ISecretProvider secretProvider,
10 | IContainerCompositionService containerCompositionService,
11 | IContainerDetailsService containerDetailsService,
12 | IManifestWriter manifestWriter)
13 | : BaseContainerProcessor(
14 | fileSystem,
15 | console,
16 | secretProvider,
17 | containerCompositionService,
18 | containerDetailsService,
19 | manifestWriter)
20 | {
21 | ///
22 | public override string ResourceType => AspireComponentLiterals.ContainerV1;
23 | }
24 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Resources/AbstractProcessors/ParameterProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Resources.AbstractProcessors;
2 |
3 | public class ParameterProcessor(IFileSystem fileSystem, IAnsiConsole console,
4 | IManifestWriter manifestWriter)
5 | : BaseResourceProcessor(fileSystem, console, manifestWriter)
6 | {
7 | ///
8 | public override string ResourceType => AspireComponentLiterals.Parameter;
9 |
10 | ///
11 | public override Resource? Deserialize(ref Utf8JsonReader reader) =>
12 | JsonSerializer.Deserialize(ref reader);
13 |
14 | public override Task CreateManifests(CreateManifestsOptions options) =>
15 | // Do nothing for Parameter Resources, they are there for configuration.
16 | Task.FromResult(true);
17 | }
18 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Resources/AbstractProcessors/ValueProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Resources.AbstractProcessors;
2 |
3 | public class ValueProcessor(IFileSystem fileSystem, IAnsiConsole console,
4 | IManifestWriter manifestWriter)
5 | : BaseResourceProcessor(fileSystem, console, manifestWriter)
6 | {
7 | ///
8 | public override string ResourceType => AspireComponentLiterals.Value;
9 |
10 | ///
11 | public override Resource? Deserialize(ref Utf8JsonReader reader) =>
12 | JsonSerializer.Deserialize(ref reader);
13 |
14 | public override Task CreateManifests(CreateManifestsOptions options) =>
15 | // Do nothing for Value Resources, they are there for configuration.
16 | Task.FromResult(true);
17 | }
18 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Resources/Dapr/DaprComponentProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Resources.Dapr;
2 |
3 | public class DaprComponentProcessor(
4 | IFileSystem fileSystem,
5 | IAnsiConsole console,
6 | IManifestWriter manifestWriter)
7 | : BaseResourceProcessor(fileSystem, console, manifestWriter)
8 | {
9 | public override string ResourceType => AspireComponentLiterals.DaprComponent;
10 |
11 | public override Resource? Deserialize(ref Utf8JsonReader reader) =>
12 | JsonSerializer.Deserialize(ref reader);
13 |
14 | public override Task CreateManifests(CreateManifestsOptions options)
15 | {
16 | var daprComponentResource = options.Resource.Value as DaprComponentResource;
17 |
18 | if (daprComponentResource?.DaprComponentProperty is null)
19 | {
20 | return Task.FromResult(false);
21 | }
22 |
23 | var templateData = new DaprComponentTemplateData()
24 | .SetType(daprComponentResource.DaprComponentProperty.Type)
25 | .SetVersion(daprComponentResource.DaprComponentProperty.Version)
26 | .SetMetadata(daprComponentResource.DaprComponentProperty.Metadata)
27 | .SetName(daprComponentResource.Name);
28 |
29 | _manifestWriter.CreateDaprManifest(options.OutputPath, templateData, daprComponentResource.Name, options.TemplatePath);
30 |
31 | LogCompletion($"{options.OutputPath}/dapr/{daprComponentResource.Name}.yaml");
32 |
33 | return Task.FromResult(true);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Resources/Dapr/DaprComponentTemplateData.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Resources.Dapr;
2 |
3 | public sealed class DaprComponentTemplateData : KubernetesDeploymentData
4 | {
5 | public string Type { get; private set; } = default!;
6 | public string? Version { get; private set; }
7 | public bool HasMetadata { get; private set; }
8 | public bool EmptyMetadata { get; private set; }
9 |
10 | public Dictionary? Metadata { get; private set; }
11 |
12 | public DaprComponentTemplateData SetType(string type)
13 | {
14 | Type = type;
15 | return this;
16 | }
17 |
18 | public DaprComponentTemplateData SetVersion(string? version)
19 | {
20 | Version = !string.IsNullOrWhiteSpace(version) ? version : "v1";
21 | return this;
22 | }
23 |
24 | public KubernetesDeploymentData SetMetadata(Dictionary? metadata)
25 | {
26 | Metadata = metadata;
27 |
28 | if (Metadata is null || Metadata.Count == 0)
29 | {
30 | EmptyMetadata = true;
31 | HasMetadata = false;
32 | return this;
33 | }
34 |
35 | HasMetadata = true;
36 | EmptyMetadata = false;
37 | return this;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Resources/Project/ProjectProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Resources.Project;
2 |
3 | ///
4 | /// A project component for version 0 of Aspire.
5 | ///
6 | public sealed class ProjectProcessor(
7 | IFileSystem fileSystem,
8 | IAnsiConsole console,
9 | ISecretProvider secretProvider,
10 | IContainerCompositionService containerCompositionService,
11 | IContainerDetailsService containerDetailsService,
12 | IManifestWriter manifestWriter)
13 | : BaseProjectProcessor(
14 | fileSystem,
15 | console,
16 | secretProvider,
17 | containerCompositionService,
18 | containerDetailsService,
19 | manifestWriter)
20 | {
21 | ///
22 | public override string ResourceType => AspireComponentLiterals.Project;
23 | }
24 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Resources/Project/ProjectV1Processor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Resources.Project;
2 |
3 | ///
4 | /// A project component for version 1 of Aspire.
5 | ///
6 | public sealed class ProjectV1Processor(
7 | IFileSystem fileSystem,
8 | IAnsiConsole console,
9 | ISecretProvider secretProvider,
10 | IContainerCompositionService containerCompositionService,
11 | IContainerDetailsService containerDetailsService,
12 | IManifestWriter manifestWriter)
13 | : BaseProjectProcessor(
14 | fileSystem,
15 | console,
16 | secretProvider,
17 | containerCompositionService,
18 | containerDetailsService,
19 | manifestWriter)
20 | {
21 | ///
22 | public override string ResourceType => AspireComponentLiterals.ProjectV1;
23 | }
24 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Transformation/Bindings/IBindingProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Transformation.Bindings;
2 |
3 | public interface IBindingProcessor
4 | {
5 | void ResetServicePort();
6 | string? ParseBinding(IReadOnlyList pathParts, JsonNode? rootNode);
7 | }
8 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Transformation/IResourceExpressionProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Transformation;
2 |
3 | public interface IResourceExpressionProcessor
4 | {
5 | void ProcessEvaluations(Dictionary resources);
6 | }
7 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Transformation/Json/IJsonExpressionProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Transformation.Json;
2 |
3 | public interface IJsonExpressionProcessor
4 | {
5 | void ResolveJsonExpressions(JsonNode? jsonNode, JsonNode rootNode);
6 | }
7 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Transformation/Json/IJsonInterpolationUnescapeProcessor.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Transformation.Json;
2 |
3 | public interface IJsonInterpolationUnescapeProcessor
4 | {
5 | void UnescapeJsonExpression(JsonNode node);
6 | }
7 |
--------------------------------------------------------------------------------
/src/Aspirate.Processors/Transformation/Literals.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Processors.Transformation;
2 |
3 | public static class Literals
4 | {
5 | public const string Bindings = "bindings";
6 | public const string Http = "http";
7 | public const string Https = "https";
8 | public const string Port = "port";
9 | public const string TargetPort = "targetPort";
10 | public const string Env = "env";
11 | public const string ConnectionString = "connectionString";
12 | public const string Host = "host";
13 | public const string Url = "url";
14 | public const string Scheme = "scheme";
15 | }
16 |
--------------------------------------------------------------------------------
/src/Aspirate.Secrets/Aspirate.Secrets.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/Aspirate.Secrets/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | global using System.IO.Abstractions;
2 | global using System.Security.Cryptography;
3 | global using System.Text;
4 | global using System.Text.Json;
5 | global using System.Text.Json.Serialization;
6 | global using Aspirate.Secrets.Protectors;
7 | global using Aspirate.Shared.Attributes;
8 | global using Aspirate.Shared.Enums;
9 | global using Aspirate.Shared.Interfaces.Secrets;
10 | global using Aspirate.Shared.Interfaces.Services;
11 | global using Aspirate.Shared.Models.Aspirate;
12 | global using Aspirate.Shared.Models.AspireManifests;
13 | global using Aspirate.Shared.Models.AspireManifests.Interfaces;
14 | global using Microsoft.Extensions.DependencyInjection;
15 | global using Spectre.Console;
16 |
--------------------------------------------------------------------------------
/src/Aspirate.Secrets/MaskedValue.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Secrets;
2 |
3 | public class MaskedValue(
4 | string? value,
5 | char maskChar = '*',
6 | int unmaskedHeadLength = 2,
7 | int unmaskedTailLength = 2)
8 | {
9 | public override string ToString()
10 | {
11 | if (value == null)
12 | {
13 | return string.Empty;
14 | }
15 |
16 | // We're going for best effort here--if the secret is exceedingly
17 | // small, we are not going to show any of it unmasked.
18 | var unmaskedTotalLength = unmaskedHeadLength + unmaskedTailLength;
19 |
20 | if (value.Length < unmaskedTotalLength * 3)
21 | {
22 | return new string(maskChar, value.Length);
23 | }
24 |
25 | var unmaskedHead = value[..unmaskedHeadLength];
26 | var unmaskedTail = value[^unmaskedTailLength..];
27 |
28 | var masked = new StringBuilder(value.Length);
29 | masked.Append(unmaskedHead);
30 | masked.Append(maskChar, value.Length - unmaskedTotalLength);
31 | masked.Append(unmaskedTail);
32 |
33 | return masked.ToString();
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Aspirate.Secrets/Protectors/BaseProtector.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Secrets.Protectors;
2 |
3 | public abstract class BaseProtector(ISecretProvider secretProvider, IAnsiConsole console) : ISecretProtectionStrategy
4 | {
5 | public abstract bool HasSecrets(KeyValuePair component);
6 |
7 | public abstract void ProtectSecrets(KeyValuePair component, bool nonInteractive);
8 |
9 | protected void UpsertSecret(KeyValuePair component, KeyValuePair input, bool nonInteractive) =>
10 | secretProvider.AddSecret(component.Key, input.Key, input.Value);
11 | }
12 |
--------------------------------------------------------------------------------
/src/Aspirate.Secrets/Protectors/ConnectionStringProtector.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Secrets.Protectors;
2 |
3 | public class ConnectionStringProtector(ISecretProvider secretProvider, IAnsiConsole console): BaseProtector(secretProvider, console)
4 | {
5 | public override bool HasSecrets(KeyValuePair component)
6 | {
7 | if (component.Value is not IResourceWithEnvironmentalVariables componentWithEnv)
8 | {
9 | return false;
10 | }
11 |
12 | return componentWithEnv.Env?.Any(x => x.Key.StartsWith(ProtectorType.ConnectionString.Value)) ?? false;
13 | }
14 |
15 | public override void ProtectSecrets(KeyValuePair component, bool nonInteractive)
16 | {
17 | if (component.Value is not IResourceWithEnvironmentalVariables componentWithEnv)
18 | {
19 | return;
20 | }
21 |
22 | var connectionStrings = componentWithEnv.Env?.Where(x => x.Key.StartsWith(ProtectorType.ConnectionString.Value, StringComparison.OrdinalIgnoreCase)).ToList();
23 |
24 | if (connectionStrings.Count == 0)
25 | {
26 | return;
27 | }
28 |
29 | foreach (var input in connectionStrings)
30 | {
31 | UpsertSecret(component, input, nonInteractive);
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/Aspirate.Secrets/Protectors/MsSqlPasswordProtector.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Secrets.Protectors;
2 |
3 | public class MsSqlPasswordProtector(ISecretProvider secretProvider, IAnsiConsole console) : BaseProtector(secretProvider, console)
4 | {
5 | public override bool HasSecrets(KeyValuePair component)
6 | {
7 | if (component.Value is not IResourceWithEnvironmentalVariables componentWithEnv)
8 | {
9 | return false;
10 | }
11 |
12 | return componentWithEnv.Env?.Any(x => x.Key.Equals(ProtectorType.MsSqlPassword.Value, StringComparison.OrdinalIgnoreCase)) ?? false;
13 | }
14 |
15 | public override void ProtectSecrets(KeyValuePair component, bool nonInteractive)
16 | {
17 | if (component.Value is not IResourceWithEnvironmentalVariables componentWithEnv)
18 | {
19 | return;
20 | }
21 |
22 | var msSqlPasswordInput = componentWithEnv.Env.FirstOrDefault(x => x.Key.Equals(ProtectorType.MsSqlPassword.Value, StringComparison.OrdinalIgnoreCase));
23 |
24 | if (!string.IsNullOrEmpty(msSqlPasswordInput.Key) && msSqlPasswordInput.Key.Equals(ProtectorType.MsSqlPassword.Value, StringComparison.OrdinalIgnoreCase))
25 | {
26 | UpsertSecret(component, msSqlPasswordInput, nonInteractive);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Aspirate.Secrets/Protectors/PostgresPasswordProtector.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Secrets.Protectors;
2 |
3 | public class PostgresPasswordProtector(ISecretProvider secretProvider, IAnsiConsole console) : BaseProtector(secretProvider, console)
4 | {
5 | public override bool HasSecrets(KeyValuePair component)
6 | {
7 | if (component.Value is not IResourceWithEnvironmentalVariables componentWithEnv)
8 | {
9 | return false;
10 | }
11 |
12 | return componentWithEnv.Env?.Any(x => x.Key.Equals(ProtectorType.PostgresPassword.Value, StringComparison.OrdinalIgnoreCase)) ?? false;
13 | }
14 |
15 | public override void ProtectSecrets(KeyValuePair component, bool nonInteractive)
16 | {
17 | if (component.Value is not IResourceWithEnvironmentalVariables componentWithEnv)
18 | {
19 | return;
20 | }
21 |
22 | var postgresPasswordInput = componentWithEnv.Env.FirstOrDefault(x => x.Key.Equals(ProtectorType.PostgresPassword.Value, StringComparison.OrdinalIgnoreCase));
23 |
24 | if (!string.IsNullOrEmpty(postgresPasswordInput.Key) && postgresPasswordInput.Key.Equals(ProtectorType.PostgresPassword.Value, StringComparison.OrdinalIgnoreCase))
25 | {
26 | UpsertSecret(component, postgresPasswordInput, nonInteractive);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Aspirate.Secrets/ServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Secrets;
2 |
3 | public static class ServiceCollectionExtensions
4 | {
5 | public static IServiceCollection AddSecretProtectionStrategies(this IServiceCollection services) =>
6 | services
7 | .AddSingleton()
8 | .AddSingleton()
9 | .AddSingleton();
10 |
11 | public static IServiceCollection AddAspirateSecretProvider(this IServiceCollection services) =>
12 | services.AddSingleton();
13 | }
14 |
--------------------------------------------------------------------------------
/src/Aspirate.Services/Aspirate.Services.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/Aspirate.Services/Implementations/PasswordGenerator.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Services.Implementations;
2 |
3 | public class PasswordGenerator : IPasswordGenerator
4 | {
5 | private const string Characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!";
6 |
7 | public string Generate(int length = 24)
8 | {
9 | var charactersLength = Characters.Length;
10 | var password = new StringBuilder(length);
11 |
12 | for (var i = 0; i < length; i++)
13 | {
14 | password.Append(Characters[RandomNumberGenerator.GetInt32(charactersLength)]);
15 | }
16 |
17 | return password.ToString();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Aspirate.Services/Implementations/ProjectPropertyService.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Services.Implementations;
2 |
3 | public sealed class ProjectPropertyService(IFileSystem filesystem, IShellExecutionService shellExecutionService, IAnsiConsole console) : IProjectPropertyService
4 | {
5 | public async Task GetProjectPropertiesAsync(string projectPath, params string[] propertyNames)
6 | {
7 | var fullProjectPath = filesystem.NormalizePath(projectPath);
8 | var propertyValues = await ExecuteDotnetMsBuildGetPropertyCommand(fullProjectPath, propertyNames);
9 |
10 | return propertyValues ?? null;
11 | }
12 |
13 | private async Task ExecuteDotnetMsBuildGetPropertyCommand(string projectPath, params string[] propertyNames)
14 | {
15 | var argumentsBuilder = ArgumentsBuilder.Create()
16 | .AppendArgument(DotNetSdkLiterals.MsBuildArgument, string.Empty, quoteValue: false)
17 | .AppendArgument($"\"{projectPath}\"", string.Empty, quoteValue: false);
18 |
19 | foreach (var propertyName in propertyNames)
20 | {
21 | argumentsBuilder.AppendArgument(DotNetSdkLiterals.GetPropertyArgument, propertyName, true);
22 | }
23 |
24 | var result = await shellExecutionService.ExecuteCommand(new()
25 | {
26 | Command = DotNetSdkLiterals.DotNetCommand,
27 | ArgumentsBuilder = argumentsBuilder,
28 | PropertyKeySeparator = ':',
29 | });
30 |
31 | if (!result.Success)
32 | {
33 | console.MarkupLine($"[red]Failed to get project properties for '{projectPath}'.[/]");
34 | ActionCausesExitException.ExitNow(result.ExitCode);
35 | }
36 |
37 | return result.Success ? result.Output : null;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Aspirate.Shared.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0
5 | false
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Attributes/RestorableStatePropertyAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Attributes;
2 |
3 | [AttributeUsage(AttributeTargets.Property)]
4 | public class RestorableStatePropertyAttribute : Attribute;
5 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Enums/ContainerBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Enums;
2 |
3 | public class ContainerBuilder : SmartEnum
4 | {
5 | private ContainerBuilder(string name, string value) : base(name, value)
6 | {
7 | }
8 |
9 | public static ContainerBuilder Docker = new(nameof(Docker), "docker");
10 | public static ContainerBuilder Podman = new(nameof(Podman), "podman");
11 | }
12 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Enums/ExistingSecretsType.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Enums;
2 |
3 | public class ExistingSecretsType : SmartEnum
4 | {
5 | private ExistingSecretsType(string name, string value) : base(name, value)
6 | {
7 | }
8 |
9 | public static ExistingSecretsType Existing = new(nameof(Existing), "Use Existing");
10 | public static ExistingSecretsType Augment = new(nameof(Augment), "Augment by adding / replacing values");
11 | public static ExistingSecretsType Overwrite = new(nameof(Overwrite), "Overwrite / Create new Password");
12 | }
13 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Enums/ImagePullPolicy.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Enums;
2 |
3 | public class ImagePullPolicy : SmartEnum
4 | {
5 | private ImagePullPolicy(string name, string value) : base(name, value)
6 | {
7 | }
8 |
9 | public static ImagePullPolicy IfNotPresent = new(nameof(IfNotPresent), "IfNotPresent");
10 | public static ImagePullPolicy Always = new(nameof(Always), "Always");
11 | public static ImagePullPolicy Never = new(nameof(Never), "Never");
12 | }
13 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Enums/OutputType.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Enums;
2 |
3 | public class OutputFormat : SmartEnum
4 | {
5 | private OutputFormat(string name, string value) : base(name, value)
6 | {
7 | }
8 |
9 | public static OutputFormat Kustomize = new(nameof(Kustomize), "kustomize");
10 | public static OutputFormat DockerCompose = new(nameof(DockerCompose), "compose");
11 | public static OutputFormat Helm = new(nameof(Helm), "helm");
12 | }
13 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Enums/ProtectorType.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Enums;
2 |
3 | public class ProtectorType(string name, string value) : SmartEnum(name, value)
4 | {
5 | public static readonly ProtectorType ConnectionString = new(nameof(ConnectionString), "ConnectionString");
6 | public static readonly ProtectorType PostgresPassword = new(nameof(PostgresPassword), "POSTGRES_PASSWORD");
7 | public static readonly ProtectorType MsSqlPassword = new(nameof(MsSqlPassword), "MSSQL_SA_PASSWORD");
8 | }
9 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Exceptions/ActionCausesExitException.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Exceptions;
2 |
3 | public class ActionCausesExitException(int exitCode, string? message = null) : Exception(message)
4 | {
5 | public int ExitCode { get; } = exitCode;
6 |
7 | [DoesNotReturn]
8 | public static void ExitNow(int exitCode = 1, string? message = null) => throw new ActionCausesExitException(exitCode);
9 | }
10 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Extensions/AnsiConsoleExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Extensions;
2 |
3 | public static class AnsiConsoleExtensions
4 | {
5 | public static void WriteRuler(this IAnsiConsole console, string message, Justify justification = Justify.Left)
6 | {
7 | var rule = new Rule(message) { Justification = justification };
8 | console.WriteLine();
9 | console.Write(rule);
10 | }
11 |
12 | public static void ValidationFailed(this IAnsiConsole console, string message)
13 | {
14 | console.MarkupLine($"[red](!)[/] {message}");
15 | ActionCausesExitException.ExitNow();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Extensions/ComposeExtensions.cs:
--------------------------------------------------------------------------------
1 | using DockerComposeBuilder.Converters;
2 |
3 | namespace Aspirate.Shared.Extensions;
4 |
5 | public static class ComposeExtensions
6 | {
7 | public static string Serialize(this Compose serializable, string lineEndings = "\n")
8 | {
9 | var serializer = new SerializerBuilder()
10 | .WithTypeConverter(new YamlValueCollectionConverter())
11 | .WithNamingConvention(UnderscoredNamingConvention.Instance)
12 | .WithEventEmitter(nextEmitter => new FlowStyleStringSequences(nextEmitter))
13 | .WithEventEmitter(nextEmitter => new FlowStringEnumConverter(nextEmitter))
14 | .WithEventEmitter(nextEmitter => new ForceQuotedStringValuesEventEmitter(nextEmitter))
15 | .WithEmissionPhaseObjectGraphVisitor(args => new YamlIEnumerableSkipEmptyObjectGraphVisitor(args.InnerVisitor))
16 | .WithNewLine(lineEndings)
17 | .Build();
18 |
19 | return serializer.Serialize(serializable);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Extensions/DockerfileParametersExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Extensions;
2 |
3 | public static class DockerfileParametersExtensions
4 | {
5 | private static readonly StringBuilder _tagBuilder = new();
6 |
7 | public static List ToImageNames(this ContainerOptions options, string resourceKey)
8 | {
9 | var images = new List();
10 |
11 | foreach (var tag in options.Tags)
12 | {
13 | _tagBuilder.Clear();
14 |
15 | if (!string.IsNullOrEmpty(options.Registry))
16 | {
17 | _tagBuilder.Append($"{options.Registry}/");
18 | }
19 |
20 | if (!string.IsNullOrEmpty(options.Prefix))
21 | {
22 | _tagBuilder.Append($"{options.Prefix}/");
23 | }
24 |
25 | if (string.IsNullOrEmpty(options.ImageName))
26 | {
27 | options.ImageName = resourceKey;
28 | }
29 |
30 | _tagBuilder.Append(options.ImageName);
31 |
32 | AppendTag(tag);
33 |
34 | images.Add(_tagBuilder.ToString());
35 | }
36 |
37 | return images;
38 | }
39 |
40 | private static void AppendTag(string? tag)
41 | {
42 | if (!string.IsNullOrEmpty(tag))
43 | {
44 | _tagBuilder.Append($":{tag}");
45 | return;
46 | }
47 |
48 | _tagBuilder.Append(":latest");
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Extensions/JsonExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Nodes;
2 |
3 | namespace Aspirate.Shared.Extensions;
4 | public static class JsonExtensions
5 | {
6 | public static JsonDocument? TryParseAsJsonDocument(this T? instance)
7 | {
8 | try
9 | {
10 | if (instance is null)
11 | {
12 | return null;
13 | }
14 |
15 | if (instance is string jsonString)
16 | {
17 | return JsonDocument.Parse(jsonString);
18 | }
19 |
20 | return JsonDocument.Parse(JsonSerializer.Serialize(instance));
21 | }
22 | catch (JsonException)
23 | {
24 | return null;
25 | }
26 | }
27 |
28 | public static JsonNode? TryParseAsJsonNode(this T? instance)
29 | {
30 | try
31 | {
32 | if (instance is null)
33 | {
34 | return null;
35 | }
36 |
37 | if (instance is string jsonString)
38 | {
39 | return JsonNode.Parse(jsonString);
40 | }
41 |
42 | return JsonNode.Parse(JsonSerializer.Serialize(instance));
43 | }
44 | catch (JsonException)
45 | {
46 | return null;
47 | }
48 | }
49 |
50 | public static T ToObject(this JsonNode jsonNode) =>
51 | JsonSerializer.Deserialize(jsonNode.ToString())!;
52 |
53 | public static T ToObject(this JsonDocument jsonDocument) =>
54 | JsonSerializer.Deserialize(jsonDocument.RootElement.GetRawText())!;
55 | }
56 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Extensions/ProtectionStrategyExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Extensions;
2 |
3 | public static class ProtectionStrategyExtensions
4 | {
5 | public static bool CheckForProtectableSecrets(
6 | this IReadOnlyCollection protectors,
7 | IReadOnlyCollection> components)
8 | {
9 | bool protectableSecrets = false;
10 |
11 | foreach (var component in components)
12 | {
13 | if (component.Value is not IResourceWithEnvironmentalVariables)
14 | {
15 | continue;
16 | }
17 |
18 | if (protectors.Any(strategy => strategy.HasSecrets(component)))
19 | {
20 | protectableSecrets = true;
21 | }
22 |
23 | if (protectableSecrets)
24 | {
25 | break;
26 | }
27 | }
28 |
29 | return protectableSecrets;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Extensions;
2 |
3 | public static class StringExtensions
4 | {
5 | public static string ToBase64(this string value)
6 | {
7 | var plainTextBytes = Encoding.UTF8.GetBytes(value);
8 | return Convert.ToBase64String(plainTextBytes);
9 | }
10 |
11 | public static string? FromBase64(this string? value)
12 | {
13 | if (string.IsNullOrEmpty(value))
14 | {
15 | return null;
16 | }
17 |
18 | var base64EncodedBytes = Convert.FromBase64String(value);
19 | return Encoding.UTF8.GetString(base64EncodedBytes);
20 | }
21 |
22 | public static string AsJsonPath(this IEnumerable pathParts)
23 | {
24 | var formattedParts = pathParts.Select(part => $"['{part}']");
25 | return "$" + string.Join("", formattedParts);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Inputs/BaseCreateOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Inputs;
2 |
3 | public abstract class BaseCreateOptions
4 | {
5 | ///
6 | /// Represents a resource in a manifest.
7 | ///
8 | public KeyValuePair Resource { get; set; }
9 |
10 | ///
11 | /// Specifies whether the resource should include a dashboard.
12 | ///
13 | public bool? WithDashboard { get; set; }
14 |
15 | ///
16 | /// Gets or sets a boolean indicating whether secrets should be disabled.
17 | ///
18 | public bool? DisableSecrets { get; set; }
19 |
20 | ///
21 | /// Gets or sets the flag indicating whether private registry should be used.
22 | ///
23 | ///
24 | /// By default, the private registry is not used. If this flag is set to , the application will pull Docker images from a private registry instead of the public Docker Hub.
25 | ///
26 | public bool? WithPrivateRegistry { get; set; }
27 |
28 | public AspirateState? CurrentState { get; set; }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Inputs/BaseKubernetesCreateOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Inputs;
2 |
3 | public abstract class BaseKubernetesCreateOptions : BaseCreateOptions
4 | {
5 | ///
6 | /// Specifies the image pull policy for a resource in a manifest.
7 | ///
8 | public required string ImagePullPolicy { get; set; }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Inputs/ContainerOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Inputs;
2 |
3 | public class ContainerOptions
4 | {
5 | public string ContainerBuilder { get; set; } = default!;
6 | public Dictionary? BuildArgs { get; set; }
7 | public string? BuildContext { get; set; }
8 | public string ImageName { get; set; } = default!;
9 | public string Registry { get; set; } = default!;
10 | public string? Prefix { get; set; }
11 | public List? Tags { get; set; } = ["latest"];
12 | }
13 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Inputs/CreateComposeEntryOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Inputs;
2 |
3 | public sealed class CreateComposeEntryOptions : BaseCreateOptions
4 | {
5 | ///
6 | /// Represents the option to include Compose builds in a manifest.
7 | ///
8 | ///
9 | /// Compose builds are used to build Docker images using Docker Compose files.
10 | ///
11 | public bool? ComposeBuilds { get; set; }
12 | }
13 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Inputs/CreateKubernetesObjectsOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Inputs;
2 |
3 | ///
4 | /// Represents options for creating Kubernetes Objects.
5 | ///
6 | public sealed class CreateKubernetesObjectsOptions : BaseKubernetesCreateOptions
7 | {
8 | public bool EncodeSecrets { get; set; } = true;
9 | }
10 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Inputs/CreateManifestsOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Inputs;
2 |
3 | ///
4 | /// Represents options for creating manifests.
5 | ///
6 | public sealed class CreateManifestsOptions : BaseKubernetesCreateOptions
7 | {
8 | ///
9 | /// Gets or sets the output path for the manifest.
10 | ///
11 | ///
12 | /// This property specifies the directory path where the manifest will be saved.
13 | ///
14 | public required string OutputPath { get; set; }
15 |
16 | ///
17 | /// Gets or sets the path to the template associated with the resource.
18 | ///
19 | ///
20 | /// This property is used to specify the path to the template file that is associated with the resource.
21 | /// It is applicable to various resource types such as ProjectResource, DockerfileResource, ContainerResource, etc.
22 | /// The template file contains the configuration and settings for the resource.
23 | ///
24 | public string? TemplatePath { get; set; }
25 | }
26 |
--------------------------------------------------------------------------------
/src/Aspirate.Shared/Inputs/KubernetesRunOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Aspirate.Shared.Inputs;
2 |
3 | public class KubernetesRunOptions
4 | {
5 | public required IKubernetes Client { get; set; }
6 |
7 | public required AspirateState CurrentState { get; set; }
8 | public required List