committed by
GitHub
11 changed files with 272 additions and 120 deletions
@ -0,0 +1,112 @@ |
|||||
|
using System; |
||||
|
using System.CommandLine; |
||||
|
using System.CommandLine.IO; |
||||
|
using System.IO; |
||||
|
using Tye.ConfigModel; |
||||
|
using YamlDotNet.Serialization; |
||||
|
using YamlDotNet.Serialization.NamingConventions; |
||||
|
|
||||
|
namespace Tye |
||||
|
{ |
||||
|
public class InitHost |
||||
|
{ |
||||
|
|
||||
|
public static string CreateTyeFile(FileInfo? path, bool force) |
||||
|
{ |
||||
|
var (content, outputFilePath) = CreateTyeFileContent(path, force); |
||||
|
|
||||
|
File.WriteAllText(outputFilePath, content); |
||||
|
|
||||
|
return outputFilePath; |
||||
|
} |
||||
|
|
||||
|
public static (string, string) CreateTyeFileContent(FileInfo? path, bool force) |
||||
|
{ |
||||
|
if (path is FileInfo && path.Exists && !force) |
||||
|
{ |
||||
|
ThrowIfTyeFilePresent(path, "tye.yml"); |
||||
|
ThrowIfTyeFilePresent(path, "tye.yaml"); |
||||
|
} |
||||
|
|
||||
|
var template = @"
|
||||
|
# tye application configuration file |
||||
|
# read all about it at https://github.com/dotnet/tye
|
||||
|
# |
||||
|
# define global settings here
|
||||
|
# name: exampleapp # application name |
||||
|
# registry: exampleuser # dockerhub username or container registry hostname |
||||
|
|
||||
|
# define multiple services here
|
||||
|
services: |
||||
|
- name: myservice |
||||
|
# project: app.csproj # msbuild project path (relative to this file) |
||||
|
# executable: app.exe # path to an executable (relative to this file) |
||||
|
# args: --arg1=3 # arguments to pass to the process |
||||
|
# replicas: 5 # number of times to launch the application |
||||
|
# env: # array of environment variables |
||||
|
# - name: key |
||||
|
# value: value |
||||
|
# bindings: # optional array of bindings (ports, connection strings) |
||||
|
# - port: 8080 # number port of the binding |
||||
|
".TrimStart();
|
||||
|
|
||||
|
// Output in the current directory unless an input file was provided, then
|
||||
|
// output next to the input file.
|
||||
|
var outputFilePath = "tye.yaml"; |
||||
|
|
||||
|
if (path is FileInfo && path.Exists) |
||||
|
{ |
||||
|
var application = ConfigFactory.FromFile(path); |
||||
|
var serializer = new SerializerBuilder() |
||||
|
.WithNamingConvention(CamelCaseNamingConvention.Instance) |
||||
|
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults) |
||||
|
.Build(); |
||||
|
|
||||
|
var extension = path.Extension.ToLowerInvariant(); |
||||
|
var directory = path.Directory; |
||||
|
|
||||
|
// Clear all bindings if any for solutions and project files
|
||||
|
if (extension == ".sln" || extension == ".csproj" || extension == ".fsproj") |
||||
|
{ |
||||
|
// If the input file is a project or solution then use that as the name
|
||||
|
application.Name = Path.GetFileNameWithoutExtension(path.Name).ToLowerInvariant(); |
||||
|
|
||||
|
foreach (var service in application.Services) |
||||
|
{ |
||||
|
service.Bindings = null!; |
||||
|
service.Configuration = null!; |
||||
|
service.Project = service.Project!.Substring(directory.FullName.Length).TrimStart('/'); |
||||
|
} |
||||
|
|
||||
|
// If the input file is a sln/project then place the config next to it
|
||||
|
outputFilePath = Path.Combine(directory.FullName, "tye.yaml"); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
// If the input file is a yaml, then use the directory name.
|
||||
|
application.Name = path.Directory.Name.ToLowerInvariant(); |
||||
|
|
||||
|
// If the input file is a yaml, then replace it.
|
||||
|
outputFilePath = path.FullName; |
||||
|
} |
||||
|
|
||||
|
template = @"
|
||||
|
# tye application configuration file |
||||
|
# read all about it at https://github.com/dotnet/tye
|
||||
|
".TrimStart() + serializer.Serialize(application);
|
||||
|
} |
||||
|
|
||||
|
return (template, outputFilePath); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private static void ThrowIfTyeFilePresent(FileInfo? path, string yml) |
||||
|
{ |
||||
|
var tyeYaml = Path.Combine(path!.DirectoryName, yml); |
||||
|
if (File.Exists(tyeYaml)) |
||||
|
{ |
||||
|
throw new CommandException($"File '{tyeYaml}' already exists. Use --force to override the {yml} file if desired."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Text; |
||||
|
|
||||
|
namespace E2ETest |
||||
|
{ |
||||
|
public static class TestHelpers |
||||
|
{ |
||||
|
// https://github.com/dotnet/aspnetcore/blob/5a0526dfd991419d5bce0d8ea525b50df2e37b04/src/Testing/src/TestPathUtilities.cs
|
||||
|
// This can get into a bad pattern for having crazy paths in places. Eventually, especially if we use helix,
|
||||
|
// we may want to avoid relying on sln position.
|
||||
|
public static string GetSolutionRootDirectory(string solution) |
||||
|
{ |
||||
|
var applicationBasePath = AppContext.BaseDirectory; |
||||
|
var directoryInfo = new DirectoryInfo(applicationBasePath); |
||||
|
|
||||
|
do |
||||
|
{ |
||||
|
var projectFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, $"{solution}.sln")); |
||||
|
if (projectFileInfo.Exists) |
||||
|
{ |
||||
|
return projectFileInfo.DirectoryName; |
||||
|
} |
||||
|
|
||||
|
directoryInfo = directoryInfo.Parent; |
||||
|
} |
||||
|
while (directoryInfo.Parent != null); |
||||
|
|
||||
|
throw new Exception($"Solution file {solution}.sln could not be found in {applicationBasePath} or its parent directories."); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,84 @@ |
|||||
|
// Licensed to the .NET Foundation under one or more agreements.
|
||||
|
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
|
// See the LICENSE file in the project root for more information.
|
||||
|
|
||||
|
using System; |
||||
|
using System.IO; |
||||
|
using System.Text.Json; |
||||
|
using Microsoft.Build.Construction; |
||||
|
using Tye; |
||||
|
using Tye.ConfigModel; |
||||
|
using Xunit; |
||||
|
using Xunit.Abstractions; |
||||
|
|
||||
|
namespace E2ETest |
||||
|
{ |
||||
|
public class TyeInitTests |
||||
|
{ |
||||
|
private readonly ITestOutputHelper output; |
||||
|
private readonly TestOutputLogEventSink sink; |
||||
|
|
||||
|
public TyeInitTests(ITestOutputHelper output) |
||||
|
{ |
||||
|
this.output = output; |
||||
|
sink = new TestOutputLogEventSink(output); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void SingleProjectInitTest() |
||||
|
{ |
||||
|
var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "single-project", "test-project")); |
||||
|
using var tempDirectory = TempDirectory.Create(); |
||||
|
DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); |
||||
|
|
||||
|
var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "test-project.csproj")); |
||||
|
|
||||
|
var (content, _) = InitHost.CreateTyeFileContent(projectFile, force: false); |
||||
|
var expectedContent = File.ReadAllText("testassets/init/single-project.yaml"); |
||||
|
|
||||
|
output.WriteLine(content); |
||||
|
|
||||
|
Assert.Equal(expectedContent, content); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void MultiProjectInitTest() |
||||
|
{ |
||||
|
var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "multi-project")); |
||||
|
using var tempDirectory = TempDirectory.Create(); |
||||
|
DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); |
||||
|
|
||||
|
// delete already present yaml
|
||||
|
File.Delete(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); |
||||
|
|
||||
|
var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "multi-project.sln")); |
||||
|
|
||||
|
var (content, _) = InitHost.CreateTyeFileContent(projectFile, force: false); |
||||
|
var expectedContent = File.ReadAllText("testassets/init/multi-project.yaml"); |
||||
|
|
||||
|
output.WriteLine(content); |
||||
|
|
||||
|
Assert.Equal(expectedContent, content); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void FrontendBackendTest() |
||||
|
{ |
||||
|
var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "frontend-backend")); |
||||
|
using var tempDirectory = TempDirectory.Create(); |
||||
|
DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath); |
||||
|
|
||||
|
// delete already present yaml
|
||||
|
File.Delete(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml")); |
||||
|
|
||||
|
var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "frontend-backend.sln")); |
||||
|
|
||||
|
var (content, _) = InitHost.CreateTyeFileContent(projectFile, force: false); |
||||
|
var expectedContent = File.ReadAllText("testassets/init/frontend-backend.yaml"); |
||||
|
|
||||
|
output.WriteLine(content); |
||||
|
|
||||
|
Assert.Equal(expectedContent, content); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
# tye application configuration file |
||||
|
# read all about it at https://github.com/dotnet/tye |
||||
|
name: frontend-backend |
||||
|
services: |
||||
|
- name: backend |
||||
|
project: backend/backend.csproj |
||||
|
- name: frontend |
||||
|
project: frontend/frontend.csproj |
||||
@ -0,0 +1,10 @@ |
|||||
|
# tye application configuration file |
||||
|
# read all about it at https://github.com/dotnet/tye |
||||
|
name: multi-project |
||||
|
services: |
||||
|
- name: backend |
||||
|
project: backend/backend.csproj |
||||
|
- name: frontend |
||||
|
project: frontend/frontend.csproj |
||||
|
- name: worker |
||||
|
project: worker/worker.csproj |
||||
@ -0,0 +1,6 @@ |
|||||
|
# tye application configuration file |
||||
|
# read all about it at https://github.com/dotnet/tye |
||||
|
name: test-project |
||||
|
services: |
||||
|
- name: test-project |
||||
|
project: test-project.csproj |
||||
Loading…
Reference in new issue