├── files-common ├── herculeslog.config.json ├── featureFlags.json ├── arborjs │ └── deps_files │ │ └── nub.png ├── bash-completion ├── win10-x64 │ └── install.cmd ├── defaultSettings.json ├── osx-x64 │ └── install.sh ├── linux-x64 │ └── install.sh └── cement_completion.lua ├── externals ├── NuGet.dll ├── NuGet.exe └── ILRepack.exe ├── docs ├── images │ └── start.png └── README-faq.md ├── module.yaml ├── scripts ├── start-ps-with-installed-path.cmd ├── start-ps-with-debug.cmd ├── start-ps-with-release.cmd ├── restore-backup.ps1 ├── make-backup.ps1 ├── set-path-debug.ps1 ├── set-path-release.ps1 ├── start_publish.cmd └── Build-Distrib.ps1 ├── .gitmodules ├── tests ├── Cement.Cli.Tests │ ├── Helpers │ │ ├── DepsFormatStyle.cs │ │ └── YamlFromText.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── CommandsTests │ │ └── TestInit.cs │ ├── nunit-tests.ps1 │ ├── UtilsTests │ │ ├── TestLastUpdateTime.cs │ │ ├── TestDirectoryJumper.cs │ │ └── TestModuleContainer.cs │ ├── SetUpFixture.cs │ ├── ParsersTests │ │ ├── TestSettingsYamlParser.cs │ │ ├── TestHooksYamlParser.cs │ │ ├── TestIniParser.cs │ │ ├── TestModuleIniParser.cs │ │ └── V2 │ │ │ ├── TestHooksSectionParser.cs │ │ │ └── TestSettingsSectionParser.cs │ ├── DepsValidatorsTests │ │ ├── TestDepsFactory.cs │ │ └── DepsValidatorTests.cs │ └── OptionsParsers │ │ └── ShowDepsCommandOptionsParserTests.cs └── Cement.Cli.Benchmarks │ ├── Cement.Cli.Benchmarks.csproj │ ├── Benchmarks.cs │ └── Benchmarks │ └── DepLineParserBenchmark.cs ├── src ├── Cement.Cli.Common │ ├── TreeishType.cs │ ├── RetryStrategy.cs │ ├── YamlParsers │ │ ├── ModuleSettings.cs │ │ ├── Models │ │ │ ├── ConfigSection.cs │ │ │ ├── ModuleConfig.cs │ │ │ ├── DepsSection.cs │ │ │ ├── ModuleDefaults.cs │ │ │ ├── ConfigSectionTitle.cs │ │ │ ├── InstallSection.cs │ │ │ ├── DepSectionItem.cs │ │ │ └── ModuleDefinition.cs │ │ ├── DepsData.cs │ │ ├── V2 │ │ │ ├── HooksSectionParser.cs │ │ │ ├── SettingsSectionParser.cs │ │ │ ├── InstallSectionMerger.cs │ │ │ ├── ConfigSectionTitleParser.cs │ │ │ ├── Factories │ │ │ │ └── ModuleYamlParserFactory.cs │ │ │ └── ConfigurationHierarchy.cs │ │ ├── ModuleYamlFile.cs │ │ ├── HooksYamlParser.cs │ │ └── SettingsYamlParser.cs │ ├── DepsValidators │ │ ├── DepsValidateResult.cs │ │ ├── IDepsValidatorFactory.cs │ │ ├── IDepsValidator.cs │ │ ├── DepsValidatorFactory.cs │ │ └── DepsValidator.cs │ ├── LocalChangesAction.cs │ ├── ProgramFilesInfo.cs │ ├── IPackageUpdater.cs │ ├── IUsagesProvider.cs │ ├── LocalChangesPolicy.cs │ ├── GlobalLocks.cs │ ├── ToolNames.cs │ ├── Exceptions │ │ ├── GitPushException.cs │ │ ├── GitAddException.cs │ │ ├── GitCommitException.cs │ │ ├── GitGCException.cs │ │ ├── GitInitException.cs │ │ ├── GitPullException.cs │ │ ├── TimeoutException.cs │ │ ├── GitCloneException.cs │ │ ├── GitBranchException.cs │ │ ├── GitRemoteException.cs │ │ ├── GitTreeishException.cs │ │ ├── CementBuildException.cs │ │ ├── CementTrackException.cs │ │ ├── GitCheckoutException.cs │ │ ├── GitLocalChangesException.cs │ │ ├── TreeishConflictException.cs │ │ ├── CementException.cs │ │ ├── BadArgumentException.cs │ │ ├── TargetNotFoundException.cs │ │ ├── BadNuGetPackageException.cs │ │ ├── NoSuchConfigurationException.cs │ │ └── BadYamlException.cs │ ├── Platform.cs │ ├── Updaters │ │ ├── ICementUpdater.cs │ │ └── LocalPathCementUpdater.cs │ ├── InfoResponseModel.cs │ ├── ShowParentsAnswer.cs │ ├── IGitRepositoryFactory.cs │ ├── Console │ │ ├── IConsole.cs │ │ └── WindowsConsole.cs │ ├── CurrentTreeish.cs │ ├── DepWithParent.cs │ ├── Rider.cs │ ├── DepWithCommitHash.cs │ ├── FeatureFlags.cs │ ├── ModulesOrder.cs │ ├── Extensions │ │ ├── StringExtensions.cs │ │ ├── DictionaryExtensions.cs │ │ ├── CommandLineExtensions.cs │ │ ├── StreamExtensions.cs │ │ └── InstallDataExtensions.cs │ ├── ReferenceWithCsproj.cs │ ├── BuildScriptWithBuildData.cs │ ├── Package.cs │ ├── BuildSettings.cs │ ├── IConfigurationParser.cs │ ├── InstallParser.cs │ ├── BuildInfoStorageData.cs │ ├── DepsReferenceSearchModel.cs │ ├── Logging │ │ └── HerculesLogConfiguration.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── PolicyMapper.cs │ ├── MsBuildLikeTool.cs │ ├── Tool.cs │ ├── GetInfo.cs │ ├── CementSettings.cs │ ├── DirectoryJumper.cs │ ├── ShellRunnerResult.cs │ ├── TimeoutHelper.cs │ ├── ModuleIniParser.cs │ ├── app.config │ ├── CheckDepsResult.cs │ ├── Graph │ │ └── GraphHelper.cs │ ├── DictionaryFriendlyContractResolver.cs │ ├── BuildData.cs │ ├── XmlDocumentHelper.cs │ ├── DepsParser.cs │ ├── FeatureFlagsProvider.cs │ ├── DepsQueue.cs │ ├── TokensList.cs │ ├── Branch.cs │ ├── IniData.cs │ ├── TempDirectory.cs │ ├── InstallHelper.cs │ ├── BuildHelper.cs │ ├── ArborJs.cs │ ├── GitHubAsset.cs │ ├── TreeishManager.cs │ ├── Module.cs │ ├── JsonConverters │ │ └── DepJsonConverter.cs │ ├── ConfigurationParser.cs │ ├── GitHubRelease.cs │ ├── GitRepositoryFactory.cs │ ├── InstallXmlParser.cs │ ├── Cement.Cli.Common.csproj │ └── IniParser.cs ├── Cement.Cli.Commands │ ├── ReInstallCommandOptions.cs │ ├── ListPackagesCommandOptions.cs │ ├── CheckPreCommitCommandOptions.cs │ ├── OptionsParsers │ │ ├── IOptionsParser.cs │ │ ├── OptionsParser.cs │ │ ├── ShowConfigsCommandOptionsParser.cs │ │ ├── ShowDepsCommandOptionsParser.cs │ │ ├── SelfUpdateCommandOptionsParser.cs │ │ ├── UsagesBuildCommandOptionsParser.cs │ │ ├── RefFixCommandOptionsParser.cs │ │ ├── CheckDepsCommandOptionsParser.cs │ │ ├── UsagesGrepCommandOptionsParser.cs │ │ ├── AddModuleCommandOptionsParser.cs │ │ ├── ChangeModuleCommandOptionsParser.cs │ │ ├── AnalyzerAddCommandOptionsParser.cs │ │ ├── UpdateCommandOptionsParser.cs │ │ ├── RefAddCommandOptionsParser.cs │ │ ├── BuildCommandOptionsParser.cs │ │ ├── BuildDepsCommandOptionsParser.cs │ │ ├── UpdateDepsCommandOptionsParser.cs │ │ ├── LsCommandOptionsParser.cs │ │ ├── PackCommandOptionsParser.cs │ │ └── UsagesShowCommandOptionsParser.cs │ ├── Attributes │ │ └── HiddenCommandAttribute.cs │ ├── ICommandActivator.cs │ ├── CommandLocation.cs │ ├── ICommand.cs │ ├── UserCommandOptions.cs │ ├── RefFixCommandOptions.cs │ ├── CompleteCommandOptions.cs │ ├── ShowConfigsCommandOptions.cs │ ├── ShowDepsCommandOptions.cs │ ├── RemovePackageCommandOptions.cs │ ├── FixReferenceResult.cs │ ├── LsCommandOptions.cs │ ├── Extensions │ │ └── CommandActivatorExtensions.cs │ ├── SelfUpdateCommandOptions.cs │ ├── AddPackageCommandOptions.cs │ ├── UsagesBuildCommandOptions.cs │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── BuildCommandOptions.cs │ ├── AnalyzerAddCommandOptions.cs │ ├── RefAddCommandOptions.cs │ ├── AddModuleCommandOptions.cs │ ├── CheckDepsCommandOptions.cs │ ├── DefaultCommandActivator.cs │ ├── UpdateCommandOptions.cs │ ├── ChangeModuleCommandOptions.cs │ ├── UsagesGrepCommandOptions.cs │ ├── PackCommandOptions.cs │ ├── UsagesShowCommandOptions.cs │ ├── BuildDepsCommandOptions.cs │ ├── app.config │ ├── DefaultLogger.cs │ ├── UpdateDepsCommandOptions.cs │ ├── GetCommandOptions.cs │ ├── VersionCommand.cs │ ├── Cement.Cli.Commands.csproj │ ├── ListPackagesCommand.cs │ ├── Command.cs │ ├── ReInstallCommand.cs │ ├── RemovePackageCommand.cs │ ├── FixReferenceResultPrinter.cs │ ├── AddPackageCommand.cs │ ├── CommandHelper.cs │ ├── SelfUpdate.cs │ ├── InitCommand.cs │ ├── ModuleCommand.cs │ ├── UserCommand.cs │ ├── CompleteCommand.cs │ ├── AddModuleCommand.cs │ ├── ChangeModuleCommand.cs │ └── PackagesCommand.cs └── Cement.Cli │ ├── Properties │ └── AssemblyInfo.cs │ ├── ServiceCollectionExtensions.cs │ └── Cement.Cli.csproj ├── Dockerfile ├── .gitignore ├── .editorconfig ├── .gitattributes ├── AUTHORS.md ├── GitVersion.yml ├── .code-style └── CSharpCodeStyle.SpaceIndent.DotSettings └── LICENSE /files-common/herculeslog.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "Enabled": false 3 | } -------------------------------------------------------------------------------- /files-common/featureFlags.json: -------------------------------------------------------------------------------- 1 | { 2 | "CleanBeforeBuild": false 3 | } -------------------------------------------------------------------------------- /externals/NuGet.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skbkontur/cement/HEAD/externals/NuGet.dll -------------------------------------------------------------------------------- /externals/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skbkontur/cement/HEAD/externals/NuGet.exe -------------------------------------------------------------------------------- /docs/images/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skbkontur/cement/HEAD/docs/images/start.png -------------------------------------------------------------------------------- /externals/ILRepack.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skbkontur/cement/HEAD/externals/ILRepack.exe -------------------------------------------------------------------------------- /module.yaml: -------------------------------------------------------------------------------- 1 | full-build: 2 | deps: 3 | 4 | build: 5 | target: Cement.Cli.sln 6 | configuration: Release 7 | -------------------------------------------------------------------------------- /files-common/arborjs/deps_files/nub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/skbkontur/cement/HEAD/files-common/arborjs/deps_files/nub.png -------------------------------------------------------------------------------- /scripts/start-ps-with-installed-path.cmd: -------------------------------------------------------------------------------- 1 | set PATH=%PATH%;%USERPROFILE%\bin\ 2 | powershell -NoProfile -ExecutionPolicy RemoteSigned -------------------------------------------------------------------------------- /files-common/bash-completion: -------------------------------------------------------------------------------- 1 | _cm() 2 | { 3 | COMPREPLY=( $(cm complete "${COMP_LINE}" ${COMP_POINT}) ) 4 | } 5 | 6 | complete -F _cm cm -------------------------------------------------------------------------------- /scripts/start-ps-with-debug.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | powershell -NoLogo -NoExit -NoProfile -ExecutionPolicy RemoteSigned -File set-path-debug.ps1 -------------------------------------------------------------------------------- /scripts/start-ps-with-release.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | powershell -NoLogo -NoExit -NoProfile -ExecutionPolicy RemoteSigned -File set-path-release.ps1 -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "vostok.devtools"] 2 | path = dependencies/vostok.devtools 3 | url = https://github.com/vostok/devtools.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/Helpers/DepsFormatStyle.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Tests.Helpers; 2 | 3 | public enum DepsFormatStyle 4 | { 5 | Yaml = 1 6 | } 7 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/TreeishType.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public enum TreeishType 4 | { 5 | Branch, 6 | Tag, 7 | CommitHash 8 | } 9 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/RetryStrategy.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public enum RetryStrategy 4 | { 5 | None, 6 | IfTimeout, 7 | IfTimeoutOrFailed 8 | } 9 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/ModuleSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.YamlParsers; 2 | 3 | public sealed class ModuleSettings 4 | { 5 | public bool IsContentModule; 6 | } 7 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DepsValidators/DepsValidateResult.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.DepsValidators; 2 | 3 | public enum DepsValidateResult : byte 4 | { 5 | Valid, 6 | Invalid 7 | } 8 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ReInstallCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class ReInstallCommandOptions 7 | { 8 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/LocalChangesAction.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public enum LocalChangesAction 4 | { 5 | Nothing, 6 | Reset, 7 | ForceLocal, 8 | Pull 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ListPackagesCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class ListPackagesCommandOptions 7 | { 8 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/CheckPreCommitCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class CheckPreCommitCommandOptions 7 | { 8 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/IOptionsParser.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Commands.OptionsParsers; 2 | 3 | public interface IOptionsParser 4 | { 5 | TOptions Parse(string[] args); 6 | } 7 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/ProgramFilesInfo.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public sealed class ProgramFilesInfo 4 | { 5 | public string x86 { get; set; } 6 | public string x64 { get; set; } 7 | } 8 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/IPackageUpdater.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | [PublicAPI] 6 | public interface IPackageUpdater 7 | { 8 | void UpdatePackages(); 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DepsValidators/IDepsValidatorFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.DepsValidators; 2 | 3 | public interface IDepsValidatorFactory 4 | { 5 | IDepsValidator Create(string configurationName); 6 | } 7 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/IUsagesProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public interface IUsagesProvider 4 | { 5 | ShowParentsAnswer GetUsages(string moduleName, string checkingBranch, string configuration = "*"); 6 | } 7 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/LocalChangesPolicy.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public enum LocalChangesPolicy 4 | { 5 | FailOnLocalChanges, 6 | Reset, 7 | ForceLocal, 8 | Pull, 9 | Interactive 10 | } 11 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/Attributes/HiddenCommandAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cement.Cli.Commands.Attributes; 4 | 5 | [AttributeUsage(AttributeTargets.Class)] 6 | public sealed class HiddenCommandAttribute : Attribute 7 | { 8 | } 9 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/GlobalLocks.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | [PublicAPI] 6 | internal static class GlobalLocks 7 | { 8 | public static readonly object PackageLockObject = new(); 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/ToolNames.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | // ReSharper disable InconsistentNaming 4 | public static class ToolNames 5 | { 6 | public const string MSBuild = "msbuild"; 7 | public const string DOTNET = "dotnet"; 8 | } 9 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ICommandActivator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | [PublicAPI] 7 | public interface ICommandActivator 8 | { 9 | object Create(Type commandType); 10 | } 11 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitPushException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitPushException : CementException 4 | { 5 | public GitPushException(string s) 6 | : base(s) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitAddException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitAddException : CementException 4 | { 5 | public GitAddException(string format) 6 | : base(format) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitCommitException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitCommitException : CementException 4 | { 5 | public GitCommitException(string s) 6 | : base(s) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitGCException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitGCException : CementException 4 | { 5 | public GitGCException(string message) 6 | : base(message) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitInitException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitInitException : CementException 4 | { 5 | public GitInitException(string format) 6 | : base(format) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitPullException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitPullException : CementException 4 | { 5 | public GitPullException(string message) 6 | : base(message) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/TimeoutException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class TimeoutException : CementException 4 | { 5 | public TimeoutException(string format) 6 | : base(format) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitCloneException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitCloneException : CementException 4 | { 5 | public GitCloneException(string message) 6 | : base(message) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Platform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | public static class Platform 6 | { 7 | public static bool IsUnix() 8 | { 9 | return OperatingSystem.IsLinux() || OperatingSystem.IsMacOS(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitBranchException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitBranchException : CementException 4 | { 5 | public GitBranchException(string message) 6 | : base(message) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitRemoteException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitRemoteException : CementException 4 | { 5 | public GitRemoteException(string message) 6 | : base(message) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitTreeishException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitTreeishException : CementException 4 | { 5 | public GitTreeishException(string message) 6 | : base(message) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/CommandLocation.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public enum CommandLocation 7 | { 8 | RootModuleDirectory, 9 | WorkspaceDirectory, 10 | Any, 11 | InsideModuleDirectory 12 | } 13 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/CementBuildException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class CementBuildException : CementException 4 | { 5 | public CementBuildException(string message) 6 | : base(message) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/CementTrackException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class CementTrackException : CementException 4 | { 5 | public CementTrackException(string message) 6 | : base(message) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitCheckoutException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitCheckoutException : CementException 4 | { 5 | public GitCheckoutException(string message) 6 | : base(message) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/GitLocalChangesException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class GitLocalChangesException : CementException 4 | { 5 | public GitLocalChangesException(string s) 6 | : base(s) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Updaters/ICementUpdater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cement.Cli.Common.Updaters; 4 | 5 | public interface ICementUpdater : IDisposable 6 | { 7 | string Name { get; } 8 | 9 | string GetNewCommitHash(); 10 | byte[] GetNewCementZip(); 11 | } 12 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/TreeishConflictException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class TreeishConflictException : CementException 4 | { 5 | public TreeishConflictException(string format) 6 | : base(format) 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/InfoResponseModel.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public sealed class InfoResponseModel 4 | { 5 | public InfoResponseModel(string commitHash) 6 | { 7 | CommitHash = commitHash; 8 | } 9 | 10 | public string CommitHash { get; } 11 | } 12 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DepsValidators/IDepsValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Cement.Cli.Common.DepsValidators; 4 | 5 | public interface IDepsValidator 6 | { 7 | DepsValidateResult Validate(IEnumerable deps, out IReadOnlyCollection validateErrors); 8 | } 9 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/ShowParentsAnswer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | public sealed class ShowParentsAnswer 7 | { 8 | public DateTime UpdatedTime; 9 | public List>> Items = new(); 10 | } 11 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/IGitRepositoryFactory.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | [PublicAPI] 6 | public interface IGitRepositoryFactory 7 | { 8 | GitRepository Create(string moduleName, string workspace); 9 | GitRepository Create(string repoPath); 10 | } 11 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ICommand.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public interface ICommand 7 | { 8 | bool MeasureElapsedTime { get; } 9 | 10 | string Name { get; } 11 | 12 | string HelpMessage { get; } 13 | 14 | int Run(string[] args); 15 | } 16 | -------------------------------------------------------------------------------- /src/Cement.Cli/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | // Setting ComVisible to false makes the types in this assembly not visible 4 | // to COM components. If you need to access a type in this assembly from 5 | // COM, set the ComVisible attribute to true on that type. 6 | [assembly: ComVisible(false)] 7 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Console/IConsole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cement.Cli.Common.Console; 4 | 5 | public interface IConsole 6 | { 7 | void Write(string message, ConsoleColor? backgroundColor = default, ConsoleColor? foregroundColor = default); 8 | 9 | void ClearLine(); 10 | 11 | int WindowWidth { get; } 12 | } 13 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/CementException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cement.Cli.Common.Exceptions; 4 | 5 | public class CementException : Exception 6 | { 7 | public CementException(string message) 8 | : base(message) 9 | { 10 | } 11 | 12 | protected CementException() 13 | { 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/BadArgumentException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class BadArgumentException : CementException 4 | { 5 | public BadArgumentException() 6 | { 7 | } 8 | 9 | public BadArgumentException(string message) 10 | : base(message) 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/TargetNotFoundException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class TargetNotFoundException : CementException 4 | { 5 | public TargetNotFoundException(string moduleName) 6 | : base($"Build target is not specified in {moduleName}/module.yaml") 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/UserCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class UserCommandOptions 7 | { 8 | public UserCommandOptions(string[] arguments) 9 | { 10 | Arguments = arguments; 11 | } 12 | 13 | public string[] Arguments { get; } 14 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/CurrentTreeish.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public sealed class CurrentTreeish 4 | { 5 | public readonly string Value; 6 | public readonly TreeishType Type; 7 | 8 | public CurrentTreeish(TreeishType type, string value) 9 | { 10 | Value = value; 11 | Type = type; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/RefFixCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class RefFixCommandOptions 7 | { 8 | public RefFixCommandOptions(bool fixExternal) 9 | { 10 | FixExternal = fixExternal; 11 | } 12 | 13 | public bool FixExternal { get; } 14 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DepWithParent.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public sealed class DepWithParent 4 | { 5 | public DepWithParent(Dep dep, string parentModule) 6 | { 7 | Dep = dep; 8 | ParentModule = parentModule; 9 | } 10 | 11 | public Dep Dep { get; } 12 | public string ParentModule { get; } 13 | } 14 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Rider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | public static class Rider 7 | { 8 | private static readonly Lazy isRunning = new( 9 | () => Process.GetProcessesByName("rider64").Length != 0); 10 | 11 | public static bool IsRunning => isRunning.Value; 12 | } 13 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/CompleteCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class CompleteCommandOptions 7 | { 8 | public CompleteCommandOptions(string[] otherArgs) 9 | { 10 | OtherArgs = otherArgs; 11 | } 12 | 13 | public string[] OtherArgs { get; } 14 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DepWithCommitHash.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public sealed class DepWithCommitHash 4 | { 5 | public readonly Dep Dep; 6 | public readonly string CommitHash; 7 | 8 | public DepWithCommitHash(Dep dep, string commitHash) 9 | { 10 | Dep = dep; 11 | CommitHash = commitHash; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/FeatureFlags.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | [JsonObject] 6 | public sealed class FeatureFlags 7 | { 8 | public static FeatureFlags Default => new() 9 | { 10 | CleanBeforeBuild = false 11 | }; 12 | 13 | [JsonProperty] 14 | public bool CleanBeforeBuild { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/ModulesOrder.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | public sealed class ModulesOrder 6 | { 7 | public List BuildOrder; 8 | public List UpdatedModules; 9 | public Dictionary CurrentCommitHashes; 10 | public Dictionary> ConfigsGraph; 11 | } 12 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ShowConfigsCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class ShowConfigsCommandOptions 7 | { 8 | public ShowConfigsCommandOptions(string moduleName) 9 | { 10 | ModuleName = moduleName; 11 | } 12 | 13 | public string ModuleName { get; } 14 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ShowDepsCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class ShowDepsCommandOptions 7 | { 8 | public ShowDepsCommandOptions(string configuration) 9 | { 10 | Configuration = configuration; 11 | } 12 | 13 | public string Configuration { get; } 14 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/BadNuGetPackageException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class BadNuGetPackageException : CementException 4 | { 5 | public BadNuGetPackageException(string packageName) 6 | : base($"Wrong package declaration: {packageName}. Package must be in format packageId/version") 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/RemovePackageCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class RemovePackageCommandOptions 7 | { 8 | public RemovePackageCommandOptions(string packageName) 9 | { 10 | PackageName = packageName; 11 | } 12 | 13 | public string PackageName { get; } 14 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/Models/ConfigSection.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.YamlParsers.Models; 2 | 3 | public sealed class ConfigSection 4 | { 5 | public ConfigSectionTitle Title { get; set; } 6 | public DepsSection DepsSection { get; set; } 7 | public BuildData[] BuildSection { get; set; } 8 | public InstallData InstallSection { get; set; } 9 | } 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mono:5.8.0.108 2 | 3 | RUN apt-get update 4 | RUN apt-get --yes install curl unzip wget 5 | 6 | RUN curl -s https://api.github.com/repos/skbkontur/cement/releases/latest | grep "browser_download_url.*zip" | cut -d : -f 2,3 | tr -d \" | wget -O cement.zip -i - 7 | RUN mkdir ./cement 8 | RUN unzip -o cement.zip -d ./cement 9 | RUN mono ../cement/dotnet/cm.exe init 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | bin/ 3 | obj/ 4 | files-kontur/ 5 | packages/ 6 | *.user 7 | AssemblyTitle.cs 8 | .vs/Cement.Net/v14/.suo 9 | .vs/ 10 | .vscode/ 11 | releases/ 12 | server.lnk 13 | deploy.bat 14 | Cement.Net/Properties/ 15 | /files-common/linux-x64/cm* 16 | /files-common/osx-x64/cm* 17 | /files-common/win10-x64/cm* 18 | /files-common/win10-x64/NuGet.exe 19 | dotnet/ 20 | *.zip 21 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/Models/ModuleConfig.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.YamlParsers.Models; 2 | 3 | public sealed class ModuleConfig 4 | { 5 | public string Name { get; set; } 6 | public bool IsDefault { get; set; } 7 | public DepsData Deps { get; set; } 8 | public InstallData Installs { get; set; } 9 | public BuildData[] Builds { get; set; } 10 | } 11 | -------------------------------------------------------------------------------- /files-common/win10-x64/install.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | rmdir /S /Q %userprofile%\.cement 3 | rmdir /S /Q %userprofile%\bin\dotnet 4 | 5 | copy /Y .\NuGet.exe ..\..\dotnet\NuGet.exe 6 | 7 | xcopy ..\..\dotnet "%userprofile%\bin\dotnet" /s /i /Y 8 | 9 | copy /Y %userprofile%\bin\dotnet\win10-x64\cm.exe %userprofile%\bin\dotnet\cm.exe 10 | 11 | .\cm.exe reinstall 12 | %userprofile%\bin\cm.cmd 13 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/NoSuchConfigurationException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class NoSuchConfigurationException : CementException 4 | { 5 | public NoSuchConfigurationException(string moduleName, string missingConfiguration) 6 | : base($"Configuration {missingConfiguration} not found in {moduleName}") 7 | { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Extensions; 2 | 3 | public static class StringExtensions 4 | { 5 | // todo: that does not apply to all strings and it shouldn't be an extension method 6 | public static bool IsFakeTarget(this string target) 7 | { 8 | return string.IsNullOrEmpty(target) || target == "None"; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/ReferenceWithCsproj.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public sealed class ReferenceWithCsproj 4 | { 5 | public readonly string CsprojFile; 6 | public readonly string Reference; 7 | 8 | public ReferenceWithCsproj(string reference, string csprojFile) 9 | { 10 | Reference = reference; 11 | CsprojFile = csprojFile; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/FixReferenceResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | internal sealed class FixReferenceResult 6 | { 7 | public Dictionary> NotFound { get; } = new(); 8 | public Dictionary> Replaced { get; } = new(); 9 | public HashSet NoYamlModules { get; } = new(); 10 | } 11 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/BuildScriptWithBuildData.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public sealed class BuildScriptWithBuildData 4 | { 5 | public BuildScriptWithBuildData(string script, BuildData buildData) 6 | { 7 | Script = script; 8 | BuildData = buildData; 9 | } 10 | 11 | public string Script { get; } 12 | 13 | public BuildData BuildData { get; } 14 | } 15 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/LsCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | public enum ModuleProcessType 6 | { 7 | Local, 8 | All, 9 | } 10 | 11 | [PublicAPI] 12 | public sealed record LsCommandOptions(bool IsSimpleMode, ModuleProcessType ModuleProcessType, bool ShowUrl, bool ShowPushUrl, 13 | string BranchName); 14 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DepsValidators/DepsValidatorFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.DepsValidators; 2 | 3 | public sealed class DepsValidatorFactory : IDepsValidatorFactory 4 | { 5 | public static DepsValidatorFactory Shared { get; } = new(); 6 | 7 | public IDepsValidator Create(string configurationName) 8 | { 9 | return new DepsValidator(configurationName); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Package.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public sealed class Package 4 | { 5 | public Package(string name, string url, string type = "git") 6 | { 7 | Name = name; 8 | Url = url; 9 | Type = type; 10 | } 11 | 12 | public string Name { get; set; } 13 | public string Url { get; set; } 14 | public string Type { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/DepsData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Cement.Cli.Common.YamlParsers; 4 | 5 | public sealed class DepsData 6 | { 7 | public DepsData(string[] force, List deps) 8 | { 9 | Force = force; 10 | Deps = deps; 11 | } 12 | 13 | public string[] Force { get; set; } 14 | public List Deps { get; set; } 15 | } 16 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/Extensions/CommandActivatorExtensions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands.Extensions; 4 | 5 | [PublicAPI] 6 | public static class CommandActivatorExtensions 7 | { 8 | public static TCommand Create(this ICommandActivator commandActivator) 9 | { 10 | return (TCommand)commandActivator.Create(typeof(TCommand)); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/SelfUpdateCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class SelfUpdateCommandOptions 7 | { 8 | public SelfUpdateCommandOptions(string branch, string server) 9 | { 10 | Branch = branch; 11 | Server = server; 12 | } 13 | 14 | public string Branch { get; } 15 | public string Server { get; } 16 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/BuildSettings.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public sealed record BuildSettings 4 | { 5 | public bool ShowObsoleteWarnings { get; set; } 6 | 7 | public bool ShowAllWarnings { get; set; } 8 | 9 | public bool ShowOutput { get; set; } 10 | 11 | public bool ShowProgress { get; set; } 12 | 13 | public bool ShowWarningsSummary { get; set; } 14 | 15 | public bool CleanBeforeBuild { get; set; } 16 | } 17 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/IConfigurationParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | public interface IConfigurationParser 6 | { 7 | IList GetConfigurations(); 8 | bool ConfigurationExists(string configName); 9 | string GetDefaultConfigurationName(); 10 | IList GetParentConfigurations(string configName); 11 | Dictionary> GetConfigurationsHierarchy(); 12 | } 13 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/InstallParser.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | public static class InstallParser 7 | { 8 | [NotNull] 9 | public static InstallData Get(string moduleName, string configuration) 10 | { 11 | var path = Path.Combine(Helper.CurrentWorkspace, moduleName); 12 | return new InstallCollector(path, moduleName).Get(configuration); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/AddPackageCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class AddPackageCommandOptions 7 | { 8 | public AddPackageCommandOptions(string packageName, string packageUrl) 9 | { 10 | PackageName = packageName; 11 | PackageUrl = packageUrl; 12 | } 13 | 14 | public string PackageName { get; } 15 | public string PackageUrl { get; } 16 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/UsagesBuildCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class UsagesBuildCommandOptions 7 | { 8 | public UsagesBuildCommandOptions(bool pause, string checkingBranch) 9 | { 10 | Pause = pause; 11 | CheckingBranch = checkingBranch; 12 | } 13 | 14 | public bool Pause { get; } 15 | 16 | public string CheckingBranch { get; } 17 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Extensions/DictionaryExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Common.Extensions; 5 | 6 | public static class DictionaryExtensions 7 | { 8 | public static TValue FindValue([NotNull] this IDictionary dict, TKey key, TValue defaultValue = default(TValue)) 9 | { 10 | return dict.TryGetValue(key, out var value) ? value : defaultValue; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/BuildInfoStorageData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | [JsonObject] 7 | public sealed class BuildInfoStorageData 8 | { 9 | public BuildInfoStorageData() 10 | { 11 | ModulesWithDeps = new Dictionary>(); 12 | } 13 | 14 | [JsonProperty("ModulesWithDeps")] 15 | public Dictionary> ModulesWithDeps { get; } 16 | } 17 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DepsReferenceSearchModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | public sealed class DepsReferenceSearchModel 6 | { 7 | public List FoundReferences; 8 | public List NotFoundInstallSection; 9 | 10 | public DepsReferenceSearchModel(List found, List notFound) 11 | { 12 | FoundReferences = found; 13 | NotFoundInstallSection = notFound; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Logging/HerculesLogConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Logging; 2 | 3 | internal sealed class HerculesLogConfiguration 4 | { 5 | public bool Enabled { get; set; } 6 | public string Stream { get; set; } 7 | public string ApiKey { get; set; } 8 | public string ServerUrl { get; set; } 9 | public string Project { get; set; } 10 | public string Environment { get; set; } 11 | public long MaximumMemoryConsumptionInBytes { get; set; } 12 | } 13 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | // Setting ComVisible to false makes the types in this assembly not visible 4 | // to COM components. If you need to access a type in this assembly from 5 | // COM, set the ComVisible attribute to true on that type. 6 | [assembly: ComVisible(false)] 7 | 8 | // The following GUID is for the ID of the typelib if this project is exposed to COM 9 | [assembly: Guid("c491085b-d936-49b0-ab17-165713773e3e")] 10 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | // Setting ComVisible to false makes the types in this assembly not visible 4 | // to COM components. If you need to access a type in this assembly from 5 | // COM, set the ComVisible attribute to true on that type. 6 | [assembly: ComVisible(false)] 7 | 8 | // The following GUID is for the ID of the typelib if this project is exposed to COM 9 | [assembly: Guid("a0e41d67-609a-4df9-95d5-bc7b40c250d8")] 10 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | // Setting ComVisible to false makes the types in this assembly not visible 4 | // to COM components. If you need to access a type in this assembly from 5 | // COM, set the ComVisible attribute to true on that type. 6 | [assembly: ComVisible(false)] 7 | 8 | // The following GUID is for the ID of the typelib if this project is exposed to COM 9 | [assembly: Guid("3890f64a-2618-4002-9705-47522d2b3750")] 10 | -------------------------------------------------------------------------------- /scripts/restore-backup.ps1: -------------------------------------------------------------------------------- 1 | $userDir = $ENV:UserProfile 2 | 3 | $binDir = Join-Path $userDir "bin" 4 | $backupDir = Join-Path $userDir "cement.backup" 5 | 6 | if(-not (Test-Path $backupDir)){ 7 | throw "Cement binaries backup directory not found at $backupDir" 8 | } 9 | 10 | if(Test-Path $binDir){ 11 | rm $binDir -Recurse -Force | Out-Null 12 | } 13 | 14 | copy $backupDir\ $binDir -Recurse -Force 15 | Write-Host "Cement binaries restored from backup $backupDir to $binDir" -ForegroundColor Green 16 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | ; 4-column space indentation 2 | [*] 3 | indent_style = space 4 | trim_trailing_whitespace = true 5 | end_of_line = lf 6 | insert_final_newline = true 7 | 8 | [*.{cs,csx,vb,vbx,tt}] 9 | indent_size = 4 10 | charset = utf-8 11 | 12 | [*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] 13 | indent_size = 2 14 | ij_xml_space_inside_empty_tag = true 15 | 16 | [*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] 17 | indent_size = 2 18 | 19 | [*.{yaml,json}] 20 | indent_size = 2 21 | -------------------------------------------------------------------------------- /scripts/make-backup.ps1: -------------------------------------------------------------------------------- 1 | $userDir = $ENV:UserProfile 2 | 3 | $binDir = Join-Path $userDir "bin" 4 | $backupDir = Join-Path $userDir "cement.backup" 5 | if(-not (Test-Path $binDir)){ 6 | throw "Cement binaries directory not found at $binDir" 7 | } 8 | 9 | if(Test-Path $backupDir){ 10 | rm -Recurse -Force $backupDir 11 | } 12 | 13 | mkdir $backupDir | Out-Null 14 | 15 | copy $binDir\* $backupDir -Recurse | Out-Null 16 | Write-Host "Cement binaries backup created at $backupDir" -ForegroundColor Green 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/BuildCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | [PublicAPI] 7 | public sealed class BuildCommandOptions 8 | { 9 | public BuildCommandOptions(string configuration, BuildSettings buildSettings) 10 | { 11 | Configuration = configuration; 12 | BuildSettings = buildSettings; 13 | } 14 | 15 | public string Configuration { get; } 16 | public BuildSettings BuildSettings { get; } 17 | } 18 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/AnalyzerAddCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | [PublicAPI] 7 | public sealed class AnalyzerAddCommandOptions 8 | { 9 | public AnalyzerAddCommandOptions(string moduleSolutionName, Dep analyzerModule) 10 | { 11 | ModuleSolutionName = moduleSolutionName; 12 | AnalyzerModule = analyzerModule; 13 | } 14 | 15 | public string ModuleSolutionName { get; } 16 | 17 | public Dep AnalyzerModule { get; } 18 | } 19 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/PolicyMapper.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | public static class PolicyMapper 4 | { 5 | public static LocalChangesPolicy GetLocalChangesPolicy(bool force, bool reset, bool pullAnyway) 6 | { 7 | if (force) 8 | return LocalChangesPolicy.ForceLocal; 9 | 10 | if (reset) 11 | return LocalChangesPolicy.Reset; 12 | 13 | if (pullAnyway) 14 | return LocalChangesPolicy.Pull; 15 | 16 | return LocalChangesPolicy.FailOnLocalChanges; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Extensions/CommandLineExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Extensions; 2 | 3 | public static class CommandLineExtensions 4 | { 5 | public static string QuoteIfNeeded(this string path) 6 | { 7 | if (path is null or {Length: < 2}) 8 | return path; 9 | 10 | if (path.StartsWith('"') && path.EndsWith('"')) 11 | return path; 12 | 13 | if (path.Contains(' ') && !path.Contains('"')) 14 | return '"' + path + '"'; 15 | 16 | return path; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/Models/DepsSection.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.YamlParsers.Models; 2 | 3 | public sealed class DepsSection 4 | { 5 | public DepsSection(string[] force = null) 6 | : this(force, new DepSectionItem[0]) 7 | { 8 | } 9 | 10 | public DepsSection(string[] force, DepSectionItem[] sectionItems) 11 | { 12 | Force = force; 13 | SectionItems = sectionItems; 14 | } 15 | 16 | public string[] Force { get; set; } 17 | public DepSectionItem[] SectionItems { get; set; } 18 | } 19 | -------------------------------------------------------------------------------- /scripts/set-path-debug.ps1: -------------------------------------------------------------------------------- 1 | $regex = "($env:USERPROFILE\bin\|$env:USERPROFILE\bin)" -replace '\\','\\'; 2 | $env:Path = $env:Path -replace $regex,''; 3 | $projRoot = (Resolve-Path $PSScriptRoot\..).Path 4 | $env:Path = "$env:Path;$projRoot\Cement.Net\bin\Debug"; 5 | 6 | 7 | try { 8 | $cmPath = $(Get-Command cm -ErrorAction SilentlyContinue).Path 9 | } 10 | finally { 11 | if($cmPath) 12 | { 13 | Write-Host "cement running from $cmPath" -ForegroundColor Blue 14 | } 15 | else 16 | { 17 | Write-Host "cm.exe command not found in PATH" -ForegroundColor Red 18 | } 19 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/OptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Cement.Cli.Common.Exceptions; 3 | 4 | namespace Cement.Cli.Commands.OptionsParsers; 5 | 6 | public abstract class OptionsParser : IOptionsParser 7 | { 8 | public abstract TOptions Parse(string[] args); 9 | 10 | protected void ThrowIfHasExtraArgs(IReadOnlyCollection extraArgs) 11 | { 12 | if (extraArgs.Count > 0) 13 | throw new BadArgumentException("Extra arguments: " + string.Join(", ", extraArgs)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Benchmarks/Cement.Cli.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/README-faq.md: -------------------------------------------------------------------------------- 1 | # Frequently asked questions 2 | 3 | #### 0. I can't build or clone some module, is it cement problem? 4 | Cement just finds your local `git` and `msbuild`, and executes commands. 5 | 6 | You can always run this commands in console, without cement. 7 | 8 | #### 1. How to use msbuild installed in custom location? 9 | Fill VS150COMNTOOLS variable with your custom location: 10 | 11 | `set VS150COMNTOOLS=D:\Program Files\Microsoft Visual Studio\2017\Professional\Common7\Tools\` 12 | 13 | Cement will run `VsDevCmd.bat` from this folder before search `msbuild.exe` file. 14 | -------------------------------------------------------------------------------- /scripts/set-path-release.ps1: -------------------------------------------------------------------------------- 1 | $regex = "($env:USERPROFILE\bin\|$env:USERPROFILE\bin)" -replace '\\','\\'; 2 | $env:Path = $env:Path -replace $regex,''; 3 | $projRoot = (Resolve-Path $PSScriptRoot\..).Path 4 | $env:Path = "$env:Path;$projRoot\Cement.Net\bin\Release"; 5 | 6 | try { 7 | $cmPath = $(Get-Command cm -ErrorAction SilentlyContinue).Path 8 | } 9 | finally { 10 | if($cmPath) 11 | { 12 | Write-Host "cement running from $cmPath" -ForegroundColor Blue 13 | } 14 | else 15 | { 16 | Write-Host "cm.exe command not found in PATH" -ForegroundColor Red 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Benchmarks/Benchmarks.cs: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/new-console-template for more information 2 | 3 | using BenchmarkDotNet.Running; 4 | using Cement.Cli.Benchmarks.Benchmarks; 5 | 6 | switch (args[0]) 7 | { 8 | case "dep": 9 | BenchmarkRunner.Run(); 10 | break; 11 | case "module": 12 | BenchmarkRunner.Run(); 13 | break; 14 | default: 15 | BenchmarkRunner.Run(); 16 | BenchmarkRunner.Run(); 17 | break; 18 | } 19 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/MsBuildLikeTool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | public sealed class MsBuildLikeTool 6 | { 7 | public MsBuildLikeTool(string path, string version = null, bool isWindowsMsBuild = false) 8 | { 9 | Path = path; 10 | Version = Version.TryParse(version ?? string.Empty, out var v) 11 | ? v 12 | : new Version(); 13 | IsWindowsMsBuild = isWindowsMsBuild; 14 | } 15 | 16 | public string Path { get; } 17 | public Version Version { get; } 18 | public bool IsWindowsMsBuild { get; } 19 | } 20 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Tool.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common; 2 | 3 | /// 4 | /// An assembling tool 5 | /// 6 | public sealed class Tool 7 | { 8 | public Tool(string name, string version = null) 9 | { 10 | Name = name; 11 | Version = version; 12 | } 13 | 14 | public Tool() 15 | { 16 | } 17 | 18 | // msbuild - for MSBuild.exe at Windows and xbuild at *nix. dotnet - for new .NET Core 19 | public string Name { get; set; } 20 | 21 | // assembling tool version, the latest at default 22 | public string Version { get; set; } 23 | } 24 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/RefAddCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | [PublicAPI] 7 | public sealed class RefAddCommandOptions 8 | { 9 | public RefAddCommandOptions(string project, Dep dep, bool testReplaces, bool force) 10 | { 11 | Project = project; 12 | Dep = dep; 13 | TestReplaces = testReplaces; 14 | Force = force; 15 | } 16 | 17 | public string Project { get; } 18 | public Dep Dep { get; } 19 | public bool TestReplaces { get; } 20 | public bool Force { get; } 21 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/GetInfo.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | [PublicAPI] 6 | public sealed class GetInfo 7 | { 8 | public bool Cloned { get; set; } 9 | 10 | public string ForcedBranch { get; set; } 11 | 12 | public bool Changed { get; set; } 13 | 14 | public bool ForcedLocal { get; set; } 15 | 16 | public bool Pulled { get; set; } 17 | 18 | public bool Reset { get; set; } 19 | 20 | public string CommitInfo { get; set; } = ""; 21 | 22 | public bool HookUpdated { get; set; } 23 | 24 | public bool Forced => ForcedBranch != null; 25 | } 26 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/V2/HooksSectionParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using JetBrains.Annotations; 4 | 5 | namespace Cement.Cli.Common.YamlParsers.V2; 6 | 7 | public sealed class HooksSectionParser 8 | { 9 | [NotNull] 10 | public string[] Parse([CanBeNull] object hooksSection) 11 | { 12 | if (hooksSection == null) 13 | return new string[0]; 14 | 15 | var hooks = hooksSection as List; 16 | if (hooks == null) 17 | return new string[0]; 18 | 19 | return hooks.Select(h => h.ToString()).ToArray(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/CommandsTests/TestInit.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Cement.Cli.Commands; 3 | using Cement.Cli.Common; 4 | using NUnit.Framework; 5 | 6 | namespace Cement.Cli.Tests.CommandsTests; 7 | 8 | [TestFixture] 9 | public class TestInit 10 | { 11 | [Test] 12 | public void CreateCementDir() 13 | { 14 | using var tmp = new TempDirectory(); 15 | using (new DirectoryJumper(tmp.Path)) 16 | { 17 | new InitCommand(ConsoleWriter.Shared).Run(new[] {"init"}); 18 | Assert.That(Directory.Exists(Path.Combine(tmp.Path, Helper.CementDirectory))); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/AddModuleCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class AddModuleCommandOptions 7 | { 8 | public AddModuleCommandOptions(string moduleName, string pushUrl, string fetchUrl, string packageName) 9 | { 10 | ModuleName = moduleName; 11 | PushUrl = pushUrl; 12 | FetchUrl = fetchUrl; 13 | PackageName = packageName; 14 | } 15 | 16 | public string ModuleName { get; } 17 | public string PushUrl { get; } 18 | public string FetchUrl { get; } 19 | public string PackageName { get; } 20 | } 21 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/CheckDepsCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class CheckDepsCommandOptions 7 | { 8 | public CheckDepsCommandOptions(string configuration, bool showAll, bool findExternal, bool showShort) 9 | { 10 | Configuration = configuration; 11 | ShowAll = showAll; 12 | FindExternal = findExternal; 13 | ShowShort = showShort; 14 | } 15 | 16 | public string Configuration { get; } 17 | public bool ShowAll { get; } 18 | public bool FindExternal { get; } 19 | public bool ShowShort { get; } 20 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/DefaultCommandActivator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | using Microsoft.Extensions.DependencyInjection; 4 | 5 | namespace Cement.Cli.Commands; 6 | 7 | [PublicAPI] 8 | public sealed class DefaultCommandActivator : ICommandActivator 9 | { 10 | private readonly IServiceProvider serviceProvider; 11 | 12 | public DefaultCommandActivator(IServiceProvider serviceProvider) 13 | { 14 | this.serviceProvider = serviceProvider; 15 | } 16 | 17 | public object Create(Type commandType) 18 | { 19 | return serviceProvider.GetRequiredService(commandType); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/UpdateCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | [PublicAPI] 7 | public sealed class UpdateCommandOptions 8 | { 9 | public UpdateCommandOptions(string treeish, bool verbose, LocalChangesPolicy policy, int? gitDepth) 10 | { 11 | Treeish = treeish; 12 | Verbose = verbose; 13 | Policy = policy; 14 | GitDepth = gitDepth; 15 | } 16 | 17 | public string Treeish { get; } 18 | public bool Verbose { get; } 19 | public LocalChangesPolicy Policy { get; } 20 | public int? GitDepth { get; } 21 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/CementSettings.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | public sealed class CementSettings 6 | { 7 | public string UserName; 8 | public string Domain; 9 | public string Password; 10 | public string EncryptedPassword; 11 | public string DefaultMsBuildVersion; 12 | public string CementServer; 13 | public bool KillMsBuild = true; 14 | public string SelfUpdateTreeish; 15 | public List Packages; 16 | public int? MaxDegreeOfParallelism; 17 | public bool? IsEnabledSelfUpdate; 18 | public Dictionary UserCommands; 19 | } 20 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/nunit-tests.ps1: -------------------------------------------------------------------------------- 1 | $script_dir = split-path -parent $MyInvocation.MyCommand.Definition 2 | $prog_files_86 = ${env:ProgramFiles(x86)} 3 | if ($prog_files_86 -eq $null) 4 | { 5 | $prog_files_86 = ${env:ProgramFiles} 6 | } 7 | $nunit_dir = (Get-ChildItem $prog_files_86\nunit*\bin | ` 8 | Sort-Object -Descending)[0] 9 | if ($nunit_dir -eq $null) 10 | { 11 | $host.ui.WriteErrorLine("Nunit framework can not be found!") 12 | Exit 1 13 | } 14 | 15 | echo $nunit_dir 16 | & "$nunit_dir\nunit-console.exe" ` 17 | bin\Debug\Tests.dll 18 | $saved = $LASTEXITCODE 19 | 20 | Remove-Item TestResult.xml 21 | 22 | Exit $saved -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ChangeModuleCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class ChangeModuleCommandOptions 7 | { 8 | public ChangeModuleCommandOptions(string moduleName, string pushUrl, string fetchUrl, string packageName) 9 | { 10 | ModuleName = moduleName; 11 | PushUrl = pushUrl; 12 | FetchUrl = fetchUrl; 13 | PackageName = packageName; 14 | } 15 | 16 | public string ModuleName { get; } 17 | public string PushUrl { get; } 18 | public string FetchUrl { get; } 19 | public string PackageName { get; } 20 | } 21 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/UsagesGrepCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class UsagesGrepCommandOptions 7 | { 8 | public UsagesGrepCommandOptions(string[] arguments, string[] fileMasks, bool skipGet, string checkingBranch) 9 | { 10 | Arguments = arguments; 11 | FileMasks = fileMasks; 12 | SkipGet = skipGet; 13 | CheckingBranch = checkingBranch; 14 | } 15 | 16 | public string[] Arguments { get; } 17 | public string[] FileMasks { get; } 18 | public bool SkipGet { get; } 19 | public string CheckingBranch { get; } 20 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Enable LF normalization 2 | * text=auto eol=lf 3 | 4 | # List of files that should be treated as text files 5 | *.sln text 6 | *.yaml text 7 | *.props text 8 | *.targets text 9 | *.config text 10 | *.dockerfile text 11 | *.cs text 12 | *.csproj text 13 | *.json text 14 | *.gitignore text 15 | *.dockerignore text 16 | *.DotSettings text 17 | *.js text 18 | *.ts text 19 | *.css text 20 | *.html text 21 | *.cshtml text 22 | *.nuspec text 23 | *.tsx text 24 | 25 | # Hint for git diff 26 | *.cs diff=csharp 27 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DirectoryJumper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | public sealed class DirectoryJumper : IDisposable 7 | { 8 | private readonly string oldCurrentDirectory; 9 | 10 | public DirectoryJumper(string path) 11 | { 12 | oldCurrentDirectory = Directory.GetCurrentDirectory(); 13 | if (!Directory.Exists(path)) 14 | { 15 | Directory.CreateDirectory(path); 16 | } 17 | 18 | Directory.SetCurrentDirectory(path); 19 | } 20 | 21 | public void Dispose() 22 | { 23 | Directory.SetCurrentDirectory(oldCurrentDirectory); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/ShowConfigsCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | 3 | namespace Cement.Cli.Commands.OptionsParsers; 4 | 5 | public sealed class ShowConfigsCommandOptionsParser : OptionsParser 6 | { 7 | public override ShowConfigsCommandOptions Parse(string[] args) 8 | { 9 | string module = null; 10 | 11 | var extraArgs = args.Skip(1).ToList(); 12 | if (extraArgs.Count > 0) 13 | { 14 | module = extraArgs[0]; 15 | ThrowIfHasExtraArgs(extraArgs.Skip(1).ToList()); 16 | } 17 | 18 | return new ShowConfigsCommandOptions(module); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/PackCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | [PublicAPI] 7 | public sealed class PackCommandOptions 8 | { 9 | public PackCommandOptions(string project, string configuration, BuildSettings buildSettings, bool preRelease) 10 | { 11 | Project = project; 12 | Configuration = configuration; 13 | BuildSettings = buildSettings; 14 | PreRelease = preRelease; 15 | } 16 | 17 | public string Project { get; } 18 | public string Configuration { get; } 19 | public BuildSettings BuildSettings { get; } 20 | public bool PreRelease { get; } 21 | } 22 | -------------------------------------------------------------------------------- /scripts/start_publish.cmd: -------------------------------------------------------------------------------- 1 | cd ..\Cement.Net 2 | if not exist ..\files-common\win10-x64 mkdir ..\files-common\win10-x64 3 | dotnet publish -r win10-x64 -o ..\files-common\win10-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true /p:DebugType=None --self-contained true 4 | if not exist ..\files-common\linux-x64 mkdir ..\files-common\linux-x64 5 | dotnet publish -r linux-x64 -o ..\files-common\linux-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true /p:DebugType=None --self-contained true 6 | if not exist ..\files-common\osx-x64 mkdir ..\files-common\osx-x64 7 | dotnet publish -r osx-x64 -o ..\files-common\osx-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true /p:DebugType=None --self-contained true 8 | timeout 2 -------------------------------------------------------------------------------- /src/Cement.Cli/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Commands; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace cm; 5 | 6 | internal static class ServiceCollectionExtensions 7 | { 8 | public static void AddCommand(this IServiceCollection services) 9 | where TCommand : class, ICommand 10 | { 11 | services.AddSingleton(); 12 | services.AddSingleton(sp => sp.GetRequiredService()); 13 | } 14 | 15 | public static void AddSubcommand(this IServiceCollection services) 16 | where TCommand : class, ICommand 17 | { 18 | services.AddSingleton(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /files-common/defaultSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "UserName": null, 3 | "Domain": null, 4 | "Password": null, 5 | "EncryptedPassword": null, 6 | "DefaultMsBuildVersion": null, 7 | "CementServer": null, 8 | "SelfUpdateTreeish": "release", 9 | "IsEnabledSelfUpdate": true, 10 | "Packages": [ 11 | { 12 | "Name": "sample", 13 | "Url": "https://github.com/KungA/cement-sample-modules", 14 | "Type": "git" 15 | }, 16 | { 17 | "Name": "vostok", 18 | "Url": "https://github.com/vostok/cement-modules.git", 19 | "Type": "git" 20 | } 21 | ], 22 | "UserCommands": { 23 | "full": "cm init && cm get {0} && cd {0} && cm build-deps && cm build" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/ShowDepsCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using NDesk.Options; 3 | 4 | namespace Cement.Cli.Commands.OptionsParsers; 5 | 6 | public sealed class ShowDepsCommandOptionsParser : OptionsParser 7 | { 8 | public override ShowDepsCommandOptions Parse(string[] args) 9 | { 10 | string configuration = null; 11 | var parser = new OptionSet 12 | { 13 | {"c|configuration=", conf => configuration = conf} 14 | }; 15 | var extraArgs = parser.Parse(args.Skip(1)); 16 | ThrowIfHasExtraArgs(extraArgs); 17 | 18 | return new ShowDepsCommandOptions(configuration); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/UsagesShowCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Commands; 4 | 5 | [PublicAPI] 6 | public sealed class UsagesShowCommandOptions 7 | { 8 | public UsagesShowCommandOptions(string module, string branch, string configuration, bool showAll, bool printEdges) 9 | { 10 | Module = module; 11 | Branch = branch; 12 | Configuration = configuration; 13 | ShowAll = showAll; 14 | PrintEdges = printEdges; 15 | } 16 | 17 | public string Module { get; } 18 | public string Branch { get; } 19 | public string Configuration { get; } 20 | public bool ShowAll { get; } 21 | public bool PrintEdges { get; } 22 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/ShellRunnerResult.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | [PublicAPI] 6 | public sealed record ShellRunnerResult(int ExitCode, string Output, string Errors, bool HasTimeout) 7 | { 8 | public void Deconstruct(out int exitCode, out string output, out string errors, out bool hasTimeout) 9 | { 10 | exitCode = ExitCode; 11 | output = Output; 12 | errors = Errors; 13 | hasTimeout = HasTimeout; 14 | } 15 | 16 | public void Deconstruct(out int exitCode, out string output, out string errors) 17 | { 18 | exitCode = ExitCode; 19 | output = Output; 20 | errors = Errors; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/BuildDepsCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | [PublicAPI] 7 | public sealed class BuildDepsCommandOptions 8 | { 9 | public BuildDepsCommandOptions(string configuration, bool rebuild, bool parallel, BuildSettings buildSettings) 10 | { 11 | Configuration = configuration; 12 | Rebuild = rebuild; 13 | Parallel = parallel; 14 | BuildSettings = buildSettings; 15 | } 16 | 17 | public string Configuration { get; } 18 | 19 | public bool Rebuild { get; } 20 | 21 | public bool Parallel { get; } 22 | 23 | public BuildSettings BuildSettings { get; } 24 | } 25 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Maintainer 2 | Cement was originally developed and is supported by SKB Kontur (https://kontur.ru/eng/about). 3 | 4 | # Original contributors 5 | - Artyom Averin (a.averin@skbkontur.ru) 6 | - Anton Chaplygin (anton92nd@skbkontur.ru) 7 | - Ivan Dashkevich (spaceorc@skbkontur.ru) 8 | - Marat Fazulzyanov (east1k@skbkontur.ru) 9 | - Ivan Goverdovsky (goverdovskiy.io@skbkontur.ru) 10 | - Dmitry Govorukhin (govorukhin@skbkontur.ru) 11 | - Alexander Kazakov (kazakov@skbkontur.ru) 12 | - Alexey Kirpichnikov (alexkir@skbkontur.ru) 13 | - George Koshelev (george.koshelev@skbkontur.ru) 14 | - Alexey Kungurtsev (kungurtsev@skbkontur.ru) 15 | - Andrey Tretjakov (tretjakov.a@skbkontur.ru) 16 | - Ibragim Yuldashev (ib@skbkontur.ru) 17 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/UtilsTests/TestLastUpdateTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Cement.Cli.Common; 4 | using FluentAssertions; 5 | using NUnit.Framework; 6 | 7 | namespace Cement.Cli.Tests.UtilsTests; 8 | 9 | [TestFixture] 10 | public class TestLastUpdateTime 11 | { 12 | [Test, NonParallelizable] 13 | public async Task TestSaveCurrent() 14 | { 15 | var now = DateTime.Now; 16 | await Task.Delay(TimeSpan.FromSeconds(1)); 17 | Helper.SaveLastUpdateTime(); 18 | 19 | var last = Helper.GetLastUpdateTime(); 20 | 21 | last.Should() 22 | .BeAfter(now) 23 | .And 24 | .BeWithin(TimeSpan.FromMinutes(2)).After(now); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/Models/ModuleDefaults.cs: -------------------------------------------------------------------------------- 1 | using JetBrains.Annotations; 2 | 3 | namespace Cement.Cli.Common.YamlParsers.Models; 4 | 5 | public sealed class ModuleDefaults 6 | { 7 | public ModuleDefaults() 8 | { 9 | HooksSection = new string[0]; 10 | SettingsSection = new ModuleSettings(); 11 | } 12 | 13 | [NotNull] 14 | public string[] HooksSection { get; set; } 15 | 16 | [NotNull] 17 | public ModuleSettings SettingsSection { get; set; } 18 | 19 | [CanBeNull] 20 | public DepsSection DepsSection { get; set; } 21 | 22 | [CanBeNull] 23 | public BuildData BuildSection { get; set; } 24 | 25 | [CanBeNull] 26 | public InstallData InstallSection { get; set; } 27 | } 28 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Exceptions/BadYamlException.cs: -------------------------------------------------------------------------------- 1 | namespace Cement.Cli.Common.Exceptions; 2 | 3 | public sealed class BadYamlException : CementException 4 | { 5 | public BadYamlException(string moduleName, string sectionName, string message) 6 | : base($"Fail to parse {sectionName} section in {moduleName}/module.yaml: {message}") 7 | { 8 | ModuleName = moduleName; 9 | SectionName = sectionName; 10 | } 11 | 12 | public BadYamlException(string sectionName, string message) 13 | : base($"Fail to parse {sectionName} section in module.yaml: {message}") 14 | { 15 | SectionName = sectionName; 16 | } 17 | 18 | public string ModuleName { get; } 19 | public string SectionName { get; } 20 | } 21 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/TimeoutHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | public static class TimeoutHelper 6 | { 7 | private const int TimesForUseBigDefault = 1; 8 | private static readonly TimeSpan SmallTimeout = TimeSpan.FromSeconds(30); 9 | private static readonly TimeSpan BigTimeout = TimeSpan.FromMinutes(10); 10 | 11 | private static int badTimes; 12 | 13 | public static TimeSpan IncreaseTimeout(TimeSpan was) 14 | { 15 | badTimes++; 16 | return was < BigTimeout ? BigTimeout : was; 17 | } 18 | 19 | public static TimeSpan GetStartTimeout() 20 | { 21 | if (badTimes > TimesForUseBigDefault) 22 | return BigTimeout; 23 | return SmallTimeout; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/ModuleIniParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Cement.Cli.Common.Exceptions; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | public static class ModuleIniParser 7 | { 8 | public static Module[] Parse(string iniFileData) 9 | { 10 | var result = new List(); 11 | var parsedData = new IniParser().ParseString(iniFileData); 12 | foreach (var section in parsedData.GetSections()) 13 | { 14 | try 15 | { 16 | var module = new Module(parsedData, section); 17 | result.Add(module); 18 | } 19 | catch (CementException) 20 | { 21 | } 22 | } 23 | 24 | return result.ToArray(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/SelfUpdateCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using NDesk.Options; 3 | 4 | namespace Cement.Cli.Commands.OptionsParsers; 5 | 6 | public sealed class SelfUpdateCommandOptionsParser : OptionsParser 7 | { 8 | public override SelfUpdateCommandOptions Parse(string[] args) 9 | { 10 | string branch = null; 11 | string server = null; 12 | 13 | var parser = new OptionSet 14 | { 15 | {"b|branch=", b => branch = b}, 16 | {"s|server=", s => server = s} 17 | }; 18 | var extraArgs = parser.Parse(args.Skip(1)); 19 | ThrowIfHasExtraArgs(extraArgs); 20 | 21 | return new SelfUpdateCommandOptions(branch, server); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/UsagesBuildCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using NDesk.Options; 3 | 4 | namespace Cement.Cli.Commands.OptionsParsers; 5 | 6 | public sealed class UsagesBuildCommandOptionsParser : OptionsParser 7 | { 8 | public override UsagesBuildCommandOptions Parse(string[] args) 9 | { 10 | string branch = null; 11 | var pause = false; 12 | 13 | var parser = new OptionSet 14 | { 15 | {"b|branch=", b => branch = b}, 16 | {"p|pause", _ => pause = true} 17 | }; 18 | var extraArgs = parser.Parse(args.Skip(2)); 19 | ThrowIfHasExtraArgs(extraArgs); 20 | 21 | return new UsagesBuildCommandOptions(pause, branch); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Cement.Cli/Cement.Cli.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | cm 6 | cm 7 | net6.0 8 | Cement.Net 9 | Copyright © 2022 10 | 5.0.0 11 | enable 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /GitVersion.yml: -------------------------------------------------------------------------------- 1 | mode: ContinuousDelivery 2 | major-version-bump-message: "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]+\\))?(!:|:.*\\n\\n((.+\\n)+\\n)?BREAKING CHANGE:\\s.+)" 3 | minor-version-bump-message: "^(feat)(\\([\\w\\s-]+\\))?:" 4 | patch-version-bump-message: "^(build|chore|ci|docs|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]+\\))?:" 5 | tag-prefix: 'v' 6 | branches: 7 | main: 8 | regex: ^master$ 9 | is-release-branch: true 10 | is-source-branch-for: ['fix', 'feature', 'dev'] 11 | fix: 12 | regex: ^fix[/-] 13 | tag: 'preview' 14 | feature: 15 | regex: ^feat(ures?)?[/-] 16 | tag: 'preview' 17 | dev: 18 | regex: ^(dev(elop)?|ci|refactor)[/-] 19 | tag: 'preview' 20 | ignore: 21 | sha: [] 22 | merge-message-formats: {} 23 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/CheckDepsResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | public sealed class CheckDepsResult 6 | { 7 | public CheckDepsResult(SortedSet notUsedDeps, IReadOnlyList notInDeps, 8 | SortedSet noYamlInstall, SortedSet configOverhead) 9 | { 10 | NotUsedDeps = notUsedDeps; 11 | NotInDeps = notInDeps; 12 | NoYamlInstallSection = noYamlInstall; 13 | ConfigOverhead = configOverhead; 14 | } 15 | 16 | public SortedSet NotUsedDeps { get; } 17 | public IReadOnlyList NotInDeps { get; } 18 | public SortedSet NoYamlInstallSection { get; } 19 | public SortedSet ConfigOverhead { get; } 20 | } 21 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Graph/GraphHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Cement.Cli.Common.Graph; 4 | 5 | public sealed class GraphHelper 6 | { 7 | public HashSet GetChildren(Dep dep, Dictionary> graph) 8 | { 9 | var result = new HashSet(); 10 | GetChildrenDfs(dep, graph, result); 11 | return result; 12 | } 13 | 14 | private static void GetChildrenDfs(Dep v, Dictionary> graph, HashSet result) 15 | { 16 | if (result.Contains(v)) 17 | return; 18 | result.Add(v); 19 | 20 | if (!graph.ContainsKey(v)) 21 | return; 22 | 23 | foreach (var u in graph[v]) 24 | { 25 | GetChildrenDfs(u, graph, result); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DictionaryFriendlyContractResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Newtonsoft.Json.Serialization; 5 | 6 | namespace Cement.Cli.Common; 7 | 8 | public sealed class DictionaryFriendlyContractResolver : CamelCasePropertyNamesContractResolver 9 | { 10 | protected override JsonContract CreateContract(Type objectType) 11 | { 12 | if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) 13 | return new JsonArrayContract(objectType); 14 | if (objectType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IDictionary<,>))) 15 | return new JsonArrayContract(objectType); 16 | return base.CreateContract(objectType); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/BuildData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Cement.Cli.Common; 4 | 5 | public sealed class BuildData 6 | { 7 | public BuildData(string target, string configuration) 8 | { 9 | Target = target; 10 | Configuration = configuration; 11 | } 12 | 13 | public BuildData(string target, string configuration, Tool tool, IReadOnlyCollection parameters, string name) 14 | { 15 | Target = target; 16 | Configuration = configuration; 17 | Tool = tool; 18 | Parameters = parameters; 19 | Name = name; 20 | } 21 | 22 | public string Target { get; } 23 | public Tool Tool { get; } 24 | public string Configuration { get; } 25 | public IReadOnlyCollection Parameters { get; } 26 | public string Name { get; } 27 | } 28 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/V2/SettingsSectionParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Common.YamlParsers.V2; 5 | 6 | public sealed class SettingsSectionParser 7 | { 8 | [NotNull] 9 | public ModuleSettings Parse([CanBeNull] object settingsSection) 10 | { 11 | if (settingsSection == null) 12 | return new ModuleSettings(); 13 | 14 | var settingsDict = settingsSection as Dictionary; 15 | if (settingsDict == null) 16 | return new ModuleSettings(); 17 | 18 | var isContentModule = settingsDict.ContainsKey("type") && ((string)settingsDict["type"]).Trim() == "content"; 19 | return new ModuleSettings 20 | { 21 | IsContentModule = isContentModule 22 | }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/SetUpFixture.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Cement.Cli.Common; 3 | using NUnit.Framework; 4 | 5 | namespace Cement.Cli.Tests; 6 | 7 | [SetUpFixture] 8 | public class SetUpFixture 9 | { 10 | [OneTimeSetUp] 11 | public void OneTimeSetUp() 12 | { 13 | EnsureDefaultSettingsExist(); 14 | } 15 | 16 | private void EnsureDefaultSettingsExist() 17 | { 18 | var defaultSettingsPath = Helper.GetCementDefaultSettingsPath(); 19 | if (File.Exists(defaultSettingsPath)) 20 | return; 21 | 22 | var defaultSettingsFolder = Path.GetDirectoryName(defaultSettingsPath)!; 23 | if (!Directory.Exists(defaultSettingsFolder)) 24 | Directory.CreateDirectory(defaultSettingsFolder); 25 | 26 | File.Copy("./files-common/defaultSettings.json", defaultSettingsPath); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/RefFixCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common.Exceptions; 3 | using NDesk.Options; 4 | 5 | namespace Cement.Cli.Commands.OptionsParsers; 6 | 7 | public sealed class RefFixCommandOptionsParser : OptionsParser 8 | { 9 | public override RefFixCommandOptions Parse(string[] args) 10 | { 11 | if (args.Length < 2 || args[0] != "ref" || args[1] != "fix") 12 | throw new BadArgumentException("Wrong usage of command.\nUsage: cm ref fix [-e]"); 13 | 14 | var external = false; 15 | var parser = new OptionSet 16 | { 17 | {"e|external", _ => external = true} 18 | }; 19 | var extraArgs = parser.Parse(args.Skip(2)); 20 | ThrowIfHasExtraArgs(extraArgs); 21 | 22 | return new RefFixCommandOptions(external); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/DefaultLogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Cement.Cli.Common.Logging; 3 | using JetBrains.Annotations; 4 | using Microsoft.Extensions.Logging; 5 | 6 | namespace Cement.Cli.Commands; 7 | 8 | [PublicAPI] 9 | public sealed class DefaultLogger : ILogger 10 | { 11 | private readonly ILogger logger; 12 | 13 | public DefaultLogger() 14 | { 15 | logger = LogManager.GetLogger(); 16 | } 17 | 18 | public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, 19 | Func formatter) 20 | { 21 | logger.Log(logLevel, eventId, state, exception, formatter); 22 | } 23 | 24 | public bool IsEnabled(LogLevel logLevel) => logger.IsEnabled(logLevel); 25 | 26 | public IDisposable BeginScope(TState state) => logger.BeginScope(state); 27 | } 28 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/UtilsTests/TestDirectoryJumper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Cement.Cli.Common; 3 | using NUnit.Framework; 4 | 5 | namespace Cement.Cli.Tests.UtilsTests; 6 | 7 | [TestFixture] 8 | public class DirectoryJumperTests 9 | { 10 | [Test] 11 | public void TestJumpToExistingDirectory() 12 | { 13 | using var tempDir = new TempDirectory(); 14 | using (new DirectoryJumper(tempDir.Path)) 15 | { 16 | Assert.AreEqual(Directory.GetCurrentDirectory(), tempDir.Path); 17 | } 18 | } 19 | 20 | [Test] 21 | public void TestJumpBack() 22 | { 23 | var startCwd = Directory.GetCurrentDirectory(); 24 | using (var tempDir = new TempDirectory()) 25 | using (new DirectoryJumper(tempDir.Path)) 26 | { 27 | } 28 | 29 | Assert.AreEqual(startCwd, Directory.GetCurrentDirectory()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/UpdateDepsCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | [PublicAPI] 7 | public sealed class UpdateDepsCommandOptions 8 | { 9 | public UpdateDepsCommandOptions(string configuration, string mergedBranch, LocalChangesPolicy policy, bool localBranchForce, 10 | bool verbose, int? gitDepth) 11 | { 12 | Configuration = configuration; 13 | MergedBranch = mergedBranch; 14 | Policy = policy; 15 | LocalBranchForce = localBranchForce; 16 | Verbose = verbose; 17 | GitDepth = gitDepth; 18 | } 19 | 20 | public string Configuration { get; } 21 | public string MergedBranch { get; } 22 | public LocalChangesPolicy Policy { get; } 23 | public bool LocalBranchForce { get; } 24 | public bool Verbose { get; } 25 | public int? GitDepth { get; } 26 | } -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/V2/InstallSectionMerger.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common.Extensions; 3 | using JetBrains.Annotations; 4 | 5 | namespace Cement.Cli.Common.YamlParsers.V2; 6 | 7 | public sealed class InstallSectionMerger 8 | { 9 | [NotNull] 10 | public InstallData Merge(InstallData currentInstalls, InstallData defaultInstalls = null, InstallData[] parentInstalls = null) 11 | { 12 | if (defaultInstalls == null && parentInstalls == null) 13 | return currentInstalls; 14 | 15 | var accumulate = defaultInstalls ?? new InstallData(); 16 | if (parentInstalls != null) 17 | accumulate = parentInstalls.Aggregate(accumulate, (cur, parent) => cur.JoinWith(parent, currentInstalls.CurrentConfigurationInstallFiles)); 18 | 19 | accumulate = accumulate.JoinWith(currentInstalls, currentInstalls.CurrentConfigurationInstallFiles); 20 | return accumulate; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /files-common/osx-x64/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf $HOME/.cement 4 | rm -rf $HOME/bin/dotnet 5 | 6 | mkdir $HOME/bin 7 | 8 | SCRIPT_DIR="$(dirname -- "$(readlink -f "${BASH_SOURCE}")")" 9 | 10 | echo "Installing cement from: $SCRIPT_DIR" 11 | 12 | echo "Copying binaries" 13 | cp -R $SCRIPT_DIR/../../dotnet $HOME/.cement 14 | cp -R $SCRIPT_DIR/../../dotnet $HOME/bin/dotnet 15 | cp -R $HOME/bin/dotnet/osx-x64/cm $HOME/bin/dotnet/cm.exe 16 | 17 | chmod -R +rw $HOME/.cement 18 | chmod +x $HOME/bin/cm 19 | chmod +x $HOME/bin/dotnet/cm.exe 20 | 21 | echo "Removing installed binary" 22 | rm -rf $HOME/bin/dotnet/osx-x64/cm 23 | 24 | echo "Running cm reinstall" 25 | chmod +x $SCRIPT_DIR/cm 26 | $SCRIPT_DIR/cm reinstall 27 | 28 | chmod -R +rw $HOME/.cement 29 | chmod +x $HOME/bin/cm 30 | chmod +x $HOME/bin/dotnet/cm.exe 31 | 32 | echo "Cement is installed. It's available at '\$HOME/bin/cm'. If you want to run it from your shell add '\$HOME/bin' to your PATH" 33 | -------------------------------------------------------------------------------- /files-common/linux-x64/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf $HOME/.cement 4 | rm -rf $HOME/bin/dotnet 5 | 6 | mkdir $HOME/bin 7 | 8 | SCRIPT_DIR="$(dirname -- "$(readlink -f "${BASH_SOURCE}")")" 9 | 10 | echo "Installing cement from: $SCRIPT_DIR" 11 | 12 | echo "Copying binaries" 13 | cp -R $SCRIPT_DIR/../../dotnet $HOME/.cement 14 | cp -R $SCRIPT_DIR/../../dotnet $HOME/bin/dotnet 15 | cp -R $HOME/bin/dotnet/linux-x64/cm $HOME/bin/dotnet/cm.exe 16 | 17 | chmod -R +rw $HOME/.cement 18 | chmod +x $HOME/bin/cm 19 | chmod +x $HOME/bin/dotnet/cm.exe 20 | 21 | echo "Removing installed binary" 22 | rm -rf $HOME/bin/dotnet/linux-x64/cm 23 | 24 | echo "Running cm reinstall" 25 | chmod +x $SCRIPT_DIR/cm 26 | $SCRIPT_DIR/cm reinstall 27 | 28 | chmod -R +rw $HOME/.cement 29 | chmod +x $HOME/bin/cm 30 | chmod +x $HOME/bin/dotnet/cm.exe 31 | 32 | echo "Cement is installed. It's available at '\$HOME/bin/cm'. If you want to run it from your shell add '\$HOME/bin' to your PATH" 33 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/GetCommandOptions.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | [PublicAPI] 7 | public sealed class GetCommandOptions 8 | { 9 | public GetCommandOptions(string configuration, LocalChangesPolicy policy, string module, string treeish, string mergedBranch, 10 | bool verbose, int? gitDepth) 11 | { 12 | Configuration = configuration; 13 | Policy = policy; 14 | Module = module; 15 | Treeish = treeish; 16 | MergedBranch = mergedBranch; 17 | Verbose = verbose; 18 | GitDepth = gitDepth; 19 | } 20 | 21 | public string Configuration { get; } 22 | public LocalChangesPolicy Policy { get; } 23 | public string Module { get; } 24 | public string Treeish { get; } 25 | public string MergedBranch { get; } 26 | public bool Verbose { get; } 27 | public int? GitDepth { get; } 28 | } 29 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/VersionCommand.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | [PublicAPI] 7 | public sealed class VersionCommand : ICommand 8 | { 9 | private readonly ConsoleWriter consoleWriter; 10 | 11 | public VersionCommand(ConsoleWriter consoleWriter) 12 | { 13 | this.consoleWriter = consoleWriter; 14 | } 15 | 16 | public bool MeasureElapsedTime { get; } 17 | 18 | public bool RequireModuleYaml { get; } 19 | 20 | public CommandLocation Location { get; } = CommandLocation.Any; 21 | 22 | public string Name => "--version"; 23 | 24 | public string HelpMessage => @" 25 | Shows cement's version 26 | 27 | Usage: 28 | cm --version 29 | "; 30 | 31 | public int Run(string[] args) 32 | { 33 | var assemblyTitle = Helper.GetAssemblyTitle(); 34 | consoleWriter.WriteLine(assemblyTitle); 35 | return 0; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/Models/ConfigSectionTitle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Common.YamlParsers.Models; 5 | 6 | public sealed class ConfigSectionTitle : IEquatable 7 | { 8 | public string Name { get; set; } 9 | 10 | [CanBeNull] 11 | public string[] Parents { get; set; } 12 | 13 | public bool IsDefault { get; set; } 14 | 15 | public override string ToString() 16 | { 17 | var result = Name; 18 | 19 | if (Parents != null) 20 | result += " > " + string.Join(", ", Parents); 21 | 22 | if (IsDefault) 23 | result += " *default"; 24 | 25 | return result; 26 | } 27 | 28 | public bool Equals(ConfigSectionTitle other) 29 | { 30 | if (ReferenceEquals(null, other)) return false; 31 | if (ReferenceEquals(this, other)) return true; 32 | 33 | return other.ToString().Equals(ToString()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/ParsersTests/TestSettingsYamlParser.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Tests.Helpers; 2 | using NUnit.Framework; 3 | 4 | namespace Cement.Cli.Tests.ParsersTests; 5 | 6 | [TestFixture] 7 | public class TestSettingsYamlParser 8 | { 9 | [Test] 10 | public void SettingsTypeContent() 11 | { 12 | const string text = @" 13 | default: 14 | settings: 15 | type: content"; 16 | 17 | Assert.IsTrue(YamlFromText.SettingsParser(text).Get().IsContentModule); 18 | } 19 | 20 | [Test] 21 | public void SettingsTypeNoContent() 22 | { 23 | const string text = @" 24 | default: 25 | settings: 26 | type: asdf"; 27 | 28 | Assert.IsFalse(YamlFromText.SettingsParser(text).Get().IsContentModule); 29 | } 30 | 31 | [Test] 32 | public void SettingsNoSection() 33 | { 34 | const string text = @" 35 | default:"; 36 | 37 | Assert.IsFalse(YamlFromText.SettingsParser(text).Get().IsContentModule); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/CheckDepsCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using NDesk.Options; 3 | 4 | namespace Cement.Cli.Commands.OptionsParsers; 5 | 6 | public sealed class CheckDepsCommandOptionsParser : OptionsParser 7 | { 8 | public override CheckDepsCommandOptions Parse(string[] args) 9 | { 10 | string configuration = null; 11 | var showAll = false; 12 | var showShort = false; 13 | var external = false; 14 | 15 | var parser = new OptionSet 16 | { 17 | {"c|configuration=", conf => configuration = conf}, 18 | {"a|all", _ => showAll = true}, 19 | {"s|short", _ => showShort = true}, 20 | {"e|external", _ => external = true} 21 | }; 22 | var extraArgs = parser.Parse(args.Skip(1)); 23 | ThrowIfHasExtraArgs(extraArgs); 24 | 25 | return new CheckDepsCommandOptions(configuration, showAll, external, showShort); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.code-style/CSharpCodeStyle.SpaceIndent.DotSettings: -------------------------------------------------------------------------------- 1 | 5 | 6 | False 8 | USE_SPACES_ONLY 9 | 10 | True 12 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/Models/InstallSection.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using JetBrains.Annotations; 4 | 5 | namespace Cement.Cli.Common.YamlParsers.Models; 6 | 7 | public sealed class InstallSection 8 | { 9 | public InstallSection(object install, object artifacts) 10 | { 11 | Install = Transform(install); 12 | Artifacts = Transform(artifacts); 13 | } 14 | 15 | public InstallSection([NotNull] string[] install, [NotNull] string[] artifacts) 16 | { 17 | Install = install; 18 | Artifacts = artifacts; 19 | } 20 | 21 | [NotNull] 22 | public string[] Install { get; } 23 | 24 | [NotNull] 25 | public string[] Artifacts { get; } 26 | 27 | [NotNull] 28 | private static string[] Transform(object sectionContent) 29 | { 30 | if (sectionContent is IEnumerable list) 31 | return list.Cast().ToArray(); 32 | 33 | return new string[0]; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/XmlDocumentHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | using System.Xml; 5 | 6 | namespace Cement.Cli.Common; 7 | 8 | public static class XmlDocumentHelper 9 | { 10 | public static XmlDocument Create(string xml) 11 | { 12 | var document = new XmlDocument(); 13 | document.LoadXml(xml); 14 | return document; 15 | } 16 | 17 | public static void Save(XmlDocument document, string filePath, string lineEndings) 18 | { 19 | var xmlContent = new StringBuilder(); 20 | using (TextWriter tempWriter = new StringWriter(xmlContent)) 21 | document.Save(tempWriter); 22 | 23 | var contentLines = xmlContent 24 | .ToString() 25 | .Split(new[] {'\r', '\n'}, StringSplitOptions.RemoveEmptyEntries); 26 | contentLines[0] = contentLines[0].Replace("utf-16", "utf-8"); 27 | File.WriteAllText(filePath, string.Join(lineEndings, contentLines), new UTF8Encoding(true)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DepsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using Cement.Cli.Common.DepsValidators; 4 | using Cement.Cli.Common.YamlParsers; 5 | 6 | namespace Cement.Cli.Common; 7 | 8 | public sealed class DepsParser 9 | { 10 | private readonly ConsoleWriter consoleWriter; 11 | private readonly IDepsValidatorFactory depsValidatorFactory; 12 | private readonly string modulePath; 13 | 14 | public DepsParser(ConsoleWriter consoleWriter, IDepsValidatorFactory depsValidatorFactory, string modulePath) 15 | { 16 | this.consoleWriter = consoleWriter; 17 | this.depsValidatorFactory = depsValidatorFactory; 18 | this.modulePath = modulePath; 19 | } 20 | 21 | public DepsData Get(string config = null) 22 | { 23 | return File.Exists(Path.Combine(modulePath, Helper.YamlSpecFile)) 24 | ? new DepsYamlParser(consoleWriter, depsValidatorFactory, new FileInfo(modulePath)).Get(config) 25 | : new DepsData(null, new List()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/Cement.Cli.Commands.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | Commands 5 | Commands 6 | Copyright © 2022 7 | warnings 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ../../externals/NuGet.dll 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/Models/DepSectionItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cement.Cli.Common.YamlParsers.Models; 4 | 5 | public sealed class DepSectionItem : IEquatable 6 | { 7 | public DepSectionItem(Dep dependency) 8 | : this(false, dependency) 9 | { 10 | } 11 | 12 | public DepSectionItem(bool isRemoved, Dep dependency) 13 | { 14 | IsRemoved = isRemoved; 15 | Dependency = dependency; 16 | } 17 | 18 | public bool IsRemoved { get; set; } 19 | public Dep Dependency { get; set; } 20 | 21 | public bool Equals(DepSectionItem other) 22 | { 23 | if (ReferenceEquals(null, other)) 24 | return false; 25 | if (ReferenceEquals(this, other)) 26 | return true; 27 | return IsRemoved == other.IsRemoved && Dependency.Equals(other.Dependency); 28 | } 29 | 30 | public override string ToString() => DebuggerDisplay; 31 | 32 | private string DebuggerDisplay => IsRemoved ? "-" + Dependency : Dependency.ToString(); 33 | } 34 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/FeatureFlagsProvider.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Microsoft.Extensions.Configuration; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | public sealed class FeatureFlagsProvider 7 | { 8 | private readonly ConsoleWriter consoleWriter; 9 | 10 | public FeatureFlagsProvider(ConsoleWriter consoleWriter) 11 | { 12 | this.consoleWriter = consoleWriter; 13 | } 14 | 15 | public FeatureFlags Get() 16 | { 17 | var featureFlagsConfigPath = Path.Combine(Helper.GetCementInstallDirectory(), "dotnet", "featureFlags.json"); 18 | if (File.Exists(featureFlagsConfigPath)) 19 | { 20 | var configuration = new ConfigurationBuilder().AddJsonFile(featureFlagsConfigPath).Build(); 21 | return configuration.Get(); 22 | } 23 | 24 | consoleWriter.WriteWarning( 25 | $"File with feature flags not found in '{featureFlagsConfigPath}'. " + 26 | "Reinstalling cement may fix that issue"); 27 | 28 | return FeatureFlags.Default; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/UtilsTests/TestModuleContainer.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using NUnit.Framework; 3 | 4 | namespace Cement.Cli.Tests.UtilsTests; 5 | 6 | [TestFixture] 7 | public class TestModuleContainer 8 | { 9 | [Test] 10 | public void TestAddToEmptyList() 11 | { 12 | var container = new ModulesContainer(); 13 | container.Add(new DepWithParent(new Dep("module"), null)); 14 | Assert.AreEqual(1, container.GetDepsByName("module").Count); 15 | } 16 | 17 | [Test] 18 | public void TestAddToNotEmptyList() 19 | { 20 | var container = new ModulesContainer(); 21 | container.Add(new DepWithParent(new Dep("module"), null)); 22 | container.Add(new DepWithParent(new Dep("module"), null)); 23 | Assert.AreEqual(2, container.GetDepsByName("module").Count); 24 | } 25 | 26 | [Test] 27 | public void TestGetDepsByNameNoDeps() 28 | { 29 | var container = new ModulesContainer(); 30 | Assert.AreEqual(0, container.GetDepsByName("module").Count); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DepsQueue.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | // TODO: не очень понятно, зачем эта обертка нужна, нужен рефакторинг 7 | public sealed class DepsQueue 8 | { 9 | private readonly Queue queue; 10 | 11 | public DepsQueue() 12 | { 13 | queue = new Queue(); 14 | } 15 | 16 | public bool IsEmpty() 17 | { 18 | return !queue.Any(); 19 | } 20 | 21 | public DepWithParent Pop() 22 | { 23 | return queue.Dequeue(); 24 | } 25 | 26 | public void AddRange(IList deps, string parentModule = null) 27 | { 28 | if (deps == null) 29 | return; 30 | 31 | foreach (var dep in deps) 32 | queue.Enqueue(new DepWithParent(dep, parentModule)); 33 | } 34 | 35 | public void AddRange(IList deps) 36 | { 37 | if (deps == null) 38 | return; 39 | 40 | foreach (var dep in deps) 41 | queue.Enqueue(dep); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/TokensList.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | internal sealed class TokensList : List>> 7 | { 8 | public static TokensList Create(IEnumerable items) 9 | { 10 | var result = new TokensList(); 11 | foreach (var item in items) 12 | { 13 | result.Add(item); 14 | } 15 | 16 | return result; 17 | } 18 | 19 | public static TokensList Create(IEnumerable items, Func next) 20 | { 21 | var result = new TokensList(); 22 | foreach (var item in items) 23 | { 24 | result.Add(item, next); 25 | } 26 | 27 | return result; 28 | } 29 | 30 | public void Add(string key, Func value) 31 | { 32 | Add(new KeyValuePair>(key, value)); 33 | } 34 | 35 | public void Add(string key) 36 | { 37 | Add(new KeyValuePair>(key, null)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Branch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Cement.Cli.Common.Exceptions; 4 | 5 | namespace Cement.Cli.Common; 6 | 7 | public sealed class Branch 8 | { 9 | private const string RefHeadsPrefix = "refs/heads/"; 10 | 11 | private Branch(string commitHash, string name) 12 | { 13 | CommitHash = commitHash; 14 | Name = name; 15 | } 16 | 17 | public static Branch Parse(string branchDescription) 18 | { 19 | var tokens = branchDescription 20 | .Split(Array.Empty(), StringSplitOptions.RemoveEmptyEntries) 21 | .ToArray(); 22 | 23 | if (tokens.Length < 2 || tokens[1].IndexOf(RefHeadsPrefix, StringComparison.Ordinal) < 0) 24 | { 25 | throw new GitBranchException("Can't parse branch from:\n" + branchDescription); 26 | } 27 | 28 | var commitHash = tokens[0]; 29 | var name = tokens[1].Replace(RefHeadsPrefix, string.Empty); 30 | 31 | return new Branch(commitHash, name); 32 | } 33 | 34 | public string CommitHash { get; } 35 | public string Name { get; } 36 | } 37 | -------------------------------------------------------------------------------- /files-common/cement_completion.lua: -------------------------------------------------------------------------------- 1 | function split(inputstr, sep) 2 | if sep == nil then 3 | sep = "%s" 4 | end 5 | local t={} ; i=1 6 | for str in string.gmatch(inputstr, "([^"..sep.."]+)") do 7 | t[i] = str 8 | i = i + 1 9 | end 10 | return t 11 | end 12 | 13 | function os.capture(cmd) 14 | local f = assert(io.popen(cmd, 'r')) 15 | local s = assert(f:read('*a')) 16 | f:close() 17 | 18 | return split(s, "\n") 19 | end 20 | 21 | function complete_all(word) 22 | return os.capture("cm complete \"" .. rl_state.line_buffer .. "\" " .. rl_state.point-1) 23 | end 24 | 25 | command_parser = clink.arg.new_parser():set_arguments( 26 | { complete_all }, 27 | { complete_all }, 28 | { complete_all }, 29 | { complete_all }, 30 | { complete_all }, 31 | { complete_all }, 32 | { complete_all }, 33 | { complete_all }, 34 | { complete_all }, 35 | { complete_all }, 36 | { complete_all }, 37 | { complete_all }, 38 | { complete_all } 39 | ) 40 | 41 | clink.arg.register_parser("cm", command_parser) -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/UsagesGrepCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using NDesk.Options; 5 | 6 | namespace Cement.Cli.Commands.OptionsParsers; 7 | 8 | public sealed class UsagesGrepCommandOptionsParser : OptionsParser 9 | { 10 | public override UsagesGrepCommandOptions Parse(string[] args) 11 | { 12 | var gitArgs = new List(); 13 | string branch = null; 14 | var skipGet = false; 15 | 16 | var parser = new OptionSet 17 | { 18 | {"b|branch=", b => branch = b}, 19 | {"s|skip-get", _ => skipGet = true}, 20 | {"<>", b => gitArgs.Add(b)} 21 | }; 22 | 23 | var delimPosition = Array.IndexOf(args, "--"); 24 | if (delimPosition < 0) 25 | delimPosition = args.Length; 26 | 27 | parser.Parse(args.Take(delimPosition)); 28 | 29 | var fileMasks = args.Skip(delimPosition + 1).ToArray(); 30 | return new UsagesGrepCommandOptions(gitArgs.ToArray(), fileMasks, skipGet, branch); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2022 SKB Kontur 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/IniData.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | public sealed class IniData 7 | { 8 | private readonly Dictionary> data; 9 | 10 | public IniData(Dictionary> data) 11 | { 12 | this.data = data; 13 | } 14 | 15 | public string GetValue(string key, string section) 16 | { 17 | return GetValue(key, section, ""); 18 | } 19 | 20 | public string[] GetKeys(string section) 21 | { 22 | if (!data.ContainsKey(section)) 23 | return new string[0]; 24 | 25 | return data[section].Keys.ToArray(); 26 | } 27 | 28 | public string[] GetSections() 29 | { 30 | return data.Keys.Where(t => t != "").ToArray(); 31 | } 32 | 33 | private string GetValue(string key, string section, string @default) 34 | { 35 | if (!data.ContainsKey(section)) 36 | return @default; 37 | 38 | if (!data[section].ContainsKey(key)) 39 | return @default; 40 | 41 | return data[section][key]; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/DepsValidatorsTests/TestDepsFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Cement.Cli.Common; 3 | 4 | namespace Cement.Cli.Tests.DepsValidatorsTests; 5 | 6 | public static class TestDepsFactory 7 | { 8 | public static Dep[] GetUniqueDeps() 9 | { 10 | var deps = new Dep[Random.Shared.Next(10, 100)]; 11 | for (var i = 0; i < deps.Length; i++) 12 | deps[i] = CreateDep(); 13 | 14 | return deps; 15 | } 16 | 17 | public static Dep[] GetNonUniqueDeps() 18 | { 19 | var deps = new Dep[Random.Shared.Next(10, 100)]; 20 | for (var i = 0; i < deps.Length - 2; i++) 21 | deps[i] = CreateDep(); 22 | 23 | var dep = CreateDep(); 24 | deps[^2] = dep; 25 | deps[^1] = dep; 26 | 27 | return deps; 28 | } 29 | 30 | private static Dep CreateDep(string name = default, string treeish = default, string configuration = default) 31 | { 32 | name ??= Guid.NewGuid().ToString(); 33 | treeish ??= Guid.NewGuid().ToString(); 34 | configuration ??= "full-build"; 35 | 36 | return new Dep(name, treeish, configuration); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ListPackagesCommand.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System.Text; 3 | using Cement.Cli.Common; 4 | using JetBrains.Annotations; 5 | 6 | namespace Cement.Cli.Commands; 7 | 8 | [PublicAPI] 9 | public sealed class ListPackagesCommand : Command 10 | { 11 | private readonly ConsoleWriter consoleWriter; 12 | 13 | public ListPackagesCommand(ConsoleWriter consoleWriter) 14 | { 15 | this.consoleWriter = consoleWriter; 16 | } 17 | 18 | public override string Name => "list"; 19 | public override string HelpMessage => ""; 20 | 21 | protected override int Execute(ListPackagesCommandOptions options) 22 | { 23 | var settings = CementSettingsRepository.Get(); 24 | 25 | var sb = new StringBuilder(); 26 | 27 | foreach (var package in settings.Packages) 28 | sb.AppendLine($"{package.Name}\t{package.Url}\t{package.Type}"); 29 | 30 | consoleWriter.Write(sb.ToString()); 31 | return 0; 32 | } 33 | 34 | protected override ListPackagesCommandOptions ParseArgs(string[] args) 35 | { 36 | return new ListPackagesCommandOptions(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/TempDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | public sealed class TempDirectory : IDisposable 7 | { 8 | public readonly string Path; 9 | 10 | public TempDirectory() 11 | { 12 | Path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString()); 13 | Directory.CreateDirectory(Path); 14 | } 15 | 16 | public void Dispose() 17 | { 18 | DeleteDirectory(Path); 19 | } 20 | 21 | private static void DeleteDirectory(string targetDir) 22 | { 23 | if (!Directory.Exists(targetDir)) 24 | return; 25 | 26 | File.SetAttributes(targetDir, FileAttributes.Normal); 27 | 28 | var files = Directory.GetFiles(targetDir); 29 | var dirs = Directory.GetDirectories(targetDir); 30 | 31 | foreach (var file in files) 32 | { 33 | File.SetAttributes(file, FileAttributes.Normal); 34 | File.Delete(file); 35 | } 36 | 37 | foreach (var dir in dirs) 38 | { 39 | DeleteDirectory(dir); 40 | } 41 | 42 | Directory.Delete(targetDir, false); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/ParsersTests/TestHooksYamlParser.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Tests.Helpers; 2 | using NUnit.Framework; 3 | 4 | namespace Cement.Cli.Tests.ParsersTests; 5 | 6 | [TestFixture] 7 | public class TestHooksYamlParser 8 | { 9 | [Test] 10 | public void HooksGet() 11 | { 12 | const string text = @" 13 | default: 14 | hooks: 15 | - a 16 | - b/c 17 | 18 | client: 19 | "; 20 | 21 | Assert.AreEqual(new[] {"a", "b/c"}, YamlFromText.HooksParser(text).Get()); 22 | } 23 | 24 | [Test] 25 | public void HooksEmpty() 26 | { 27 | const string text = @" 28 | default: 29 | hooks: 30 | 31 | client: 32 | "; 33 | 34 | Assert.That(YamlFromText.HooksParser(text).Get().Count == 0); 35 | } 36 | 37 | [Test] 38 | public void NoHooksSection() 39 | { 40 | const string text = @" 41 | default: 42 | 43 | client: 44 | "; 45 | 46 | Assert.That(YamlFromText.HooksParser(text).Get().Count == 0); 47 | } 48 | 49 | [Test] 50 | public void NoDefaultSection() 51 | { 52 | const string text = @" 53 | client: 54 | "; 55 | 56 | Assert.That(YamlFromText.HooksParser(text).Get().Count == 0); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/InstallHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using Cement.Cli.Common.YamlParsers; 5 | 6 | namespace Cement.Cli.Common; 7 | 8 | public static class InstallHelper 9 | { 10 | private static List allInstallFiles; 11 | 12 | public static List GetAllInstallFiles() 13 | { 14 | if (allInstallFiles != null) 15 | return allInstallFiles; 16 | 17 | var modules = Helper.GetModules().Select(m => m.Name).Where(Yaml.Exists).ToList(); 18 | allInstallFiles = modules.SelectMany(GetAllInstallFiles).ToList(); 19 | return allInstallFiles; 20 | } 21 | 22 | public static List GetAllInstallFiles(string module) 23 | { 24 | if (!File.Exists(Path.Combine(Helper.CurrentWorkspace, module, Helper.YamlSpecFile))) 25 | return new List(); 26 | var configs = Yaml.ConfigurationParser(module).GetConfigurations(); 27 | var result = configs.Select(config => Yaml.InstallParser(module).Get(config)).SelectMany(parser => parser.Artifacts); 28 | return result.Distinct().Select(file => Path.Combine(module, file)).ToList(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /scripts/Build-Distrib.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/pwsh 2 | 3 | <# 4 | .SYNOPSIS 5 | Build logic from CI scripts for local builds 6 | #> 7 | 8 | $configuration = "Debug" 9 | 10 | function Confirm-Path($path) 11 | { 12 | if (-not (Test-Path $path -PathType Container)) 13 | { 14 | New-Item $path -ItemType Directory 15 | } 16 | } 17 | 18 | function Reset-Path($path) 19 | { 20 | if (Test-Path $path) 21 | { 22 | Remove-Item $path -Recurse -Force 23 | Confirm-Path $path 24 | } 25 | } 26 | 27 | function Invoke-Publish($rid) 28 | { 29 | dotnet publish -c $configuration -r $rid -o ".\files-common\$rid" /p:PublishSingleFile=true /p:DebugType=None --self-contained true .\Cement.Net\CementEntry.csproj 30 | } 31 | 32 | Set-Location -Path .. 33 | 34 | git submodule update --init 35 | 36 | Reset-Path .\dotnet 37 | Confirm-Path .\files-common\win10-x64 38 | Confirm-Path .\files-common\linux-x64 39 | Confirm-Path .\files-common\osx-x64 40 | 41 | Invoke-Publish win10-x64 42 | Invoke-Publish linux-x64 43 | Invoke-Publish osx-x64 44 | 45 | Copy-Item .\externals\NuGet.exe .\files-common\win10-x64\ -Force 46 | 47 | $commit = git log -1 --pretty=format:"%H" 48 | Copy-Item .\files-common\* .\dotnet -Recurse 49 | Compress-Archive .\dotnet "$commit.zip" 50 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/Command.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Cement.Cli.Common.Logging; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Cement.Cli.Commands; 6 | 7 | public abstract class Command : ICommand 8 | where TCommandOptions : notnull 9 | { 10 | private ILogger logger; 11 | 12 | protected Command() 13 | { 14 | // backward compatibility 15 | logger = LogManager.GetLogger(GetType()); 16 | } 17 | 18 | public abstract string Name { get; } 19 | public abstract string HelpMessage { get; } 20 | public virtual bool MeasureElapsedTime { get; set; } 21 | 22 | public int Run(string[] args) 23 | { 24 | var options = LogAndParseArgs(args); 25 | return Execute(options); 26 | } 27 | 28 | protected abstract int Execute(TCommandOptions commandOptions); 29 | protected abstract TCommandOptions ParseArgs(string[] args); 30 | 31 | private TCommandOptions LogAndParseArgs(string[] args) 32 | { 33 | logger.LogDebug("Parsing args: [{Args}] in {WorkingDirectory}", string.Join(" ", args), Directory.GetCurrentDirectory()); 34 | 35 | var options = ParseArgs(args); 36 | 37 | logger.LogDebug("OK parsing args"); 38 | return options; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Updaters/LocalPathCementUpdater.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Microsoft.Extensions.Logging; 4 | 5 | namespace Cement.Cli.Common.Updaters; 6 | 7 | public sealed class LocalPathCementUpdater : ICementUpdater 8 | { 9 | private readonly ILogger log; 10 | 11 | public LocalPathCementUpdater(ILogger log) 12 | { 13 | this.log = log; 14 | } 15 | 16 | public string Name => "fileSystemLocalPath"; 17 | 18 | public string GetNewCommitHash() 19 | { 20 | return DateTime.Now.Ticks.ToString(); 21 | } 22 | 23 | public byte[] GetNewCementZip() 24 | { 25 | try 26 | { 27 | return File.ReadAllBytes(Path.Combine(GetZipCementDirectory(), "cement.zip")); 28 | } 29 | catch (Exception ex) 30 | { 31 | log.LogError(ex, "Fail self-update, exception: '{ErrorMessage}'", ex.Message); 32 | } 33 | 34 | return null; 35 | } 36 | 37 | public void Dispose() 38 | { 39 | } 40 | 41 | private static string GetZipCementDirectory() 42 | { 43 | var zipDir = Path.Combine(Helper.HomeDirectory(), "work"); 44 | 45 | if (!Directory.Exists(zipDir)) 46 | Directory.CreateDirectory(zipDir); 47 | 48 | return zipDir; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/BuildHelper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Threading; 4 | using Cement.Cli.Common.YamlParsers; 5 | 6 | namespace Cement.Cli.Common; 7 | 8 | public sealed class BuildHelper 9 | { 10 | private readonly SemaphoreSlim semaphore = new(1, 1); 11 | 12 | public static BuildHelper Shared { get; } = new(); 13 | 14 | private BuildHelper() 15 | { 16 | } 17 | 18 | public void RemoveModuleFromBuiltInfo(string moduleName) 19 | { 20 | semaphore.Wait(); 21 | try 22 | { 23 | var storage = BuildInfoStorage.Deserialize(); 24 | storage.RemoveBuildInfo(moduleName); 25 | storage.Save(); 26 | } 27 | finally 28 | { 29 | semaphore.Release(); 30 | } 31 | } 32 | 33 | public bool HasAllOutput(string moduleName, string configuration, bool requireYaml) 34 | { 35 | var path = Path.Combine(Helper.CurrentWorkspace, moduleName, Helper.YamlSpecFile); 36 | if (!File.Exists(path)) 37 | return !requireYaml; 38 | var artifacts = Yaml.InstallParser(moduleName).Get(configuration).Artifacts; 39 | return artifacts!.Select(Helper.FixPath).All(art => File.Exists(Path.Combine(Helper.CurrentWorkspace, moduleName, art))); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/AddModuleCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common.Exceptions; 3 | using NDesk.Options; 4 | 5 | namespace Cement.Cli.Commands.OptionsParsers; 6 | 7 | public sealed class AddModuleCommandOptionsParser : OptionsParser 8 | { 9 | public override AddModuleCommandOptions Parse(string[] args) 10 | { 11 | string pushUrl = null; 12 | string packageName = null; 13 | 14 | var parser = new OptionSet 15 | { 16 | {"p|pushurl=", p => pushUrl = p}, 17 | {"package=", p => packageName = p} 18 | }; 19 | 20 | var extraArgs = parser.Parse(args.Skip(1)); 21 | if (extraArgs.Count < 3) 22 | { 23 | throw new BadArgumentException( 24 | "Too few arguments. \n" + 25 | "Using: cm module module_name module_fetch_url " + 26 | "[-p|--pushurl=module_push_url] [--package=package_name]"); 27 | } 28 | 29 | var moduleName = extraArgs[1]; 30 | var fetchUrl = extraArgs[2]; 31 | 32 | extraArgs = extraArgs.Skip(3).ToList(); 33 | ThrowIfHasExtraArgs(extraArgs); 34 | 35 | return new AddModuleCommandOptions(moduleName, pushUrl, fetchUrl, packageName); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/ChangeModuleCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common.Exceptions; 3 | using NDesk.Options; 4 | 5 | namespace Cement.Cli.Commands.OptionsParsers; 6 | 7 | public sealed class ChangeModuleCommandOptionsParser : OptionsParser 8 | { 9 | public override ChangeModuleCommandOptions Parse(string[] args) 10 | { 11 | string pushUrl = null; 12 | string packageName = null; 13 | 14 | var parser = new OptionSet 15 | { 16 | {"p|pushurl=", p => pushUrl = p}, 17 | {"package=", p => packageName = p} 18 | }; 19 | 20 | var extraArgs = parser.Parse(args.Skip(1)); 21 | if (extraArgs.Count < 3) 22 | { 23 | throw new BadArgumentException( 24 | "Too few arguments. \n" + 25 | "Using: cm module module_name module_fetch_url " + 26 | "[-p|--pushurl=module_push_url] [--package=package_name]"); 27 | } 28 | 29 | var moduleName = extraArgs[1]; 30 | var fetchUrl = extraArgs[2]; 31 | 32 | extraArgs = extraArgs.Skip(3).ToList(); 33 | ThrowIfHasExtraArgs(extraArgs); 34 | 35 | return new ChangeModuleCommandOptions(moduleName, pushUrl, fetchUrl, packageName); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/AnalyzerAddCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using Cement.Cli.Common.Exceptions; 3 | using NDesk.Options; 4 | 5 | namespace Cement.Cli.Commands.OptionsParsers; 6 | 7 | public sealed class AnalyzerAddCommandOptionsParser : OptionsParser 8 | { 9 | public override AnalyzerAddCommandOptions Parse(string[] args) 10 | { 11 | string configuration = null; 12 | var parser = new OptionSet 13 | { 14 | {"c|configuration=", conf => configuration = conf} 15 | }; 16 | 17 | args = parser.Parse(args).ToArray(); 18 | 19 | if (args.Length is < 3 or > 4 || args[0] != "analyzer" || args[1] != "add") 20 | { 21 | throw new BadArgumentException( 22 | $"Command format error: cm {string.Join(" ", args)}\n" + 23 | $"Command format: cm analyzer add [/configuration] []"); 24 | } 25 | 26 | var module = args[2]; 27 | var solution = args.Length == 4 ? args[3] : null; 28 | 29 | var analyzerModule = new Dep(module); 30 | 31 | if (configuration != null) 32 | analyzerModule.Configuration = configuration; 33 | 34 | return new AnalyzerAddCommandOptions(solution, analyzerModule); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/ArborJs.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using Cement.Cli.Common.Exceptions; 5 | 6 | namespace Cement.Cli.Common; 7 | 8 | public sealed class ArborJs 9 | { 10 | public void Show(string moduleName, List lines) 11 | { 12 | var arborDir = Path.Combine(Helper.GetCementInstallDirectory(), "dotnet", "arborjs"); 13 | var templateFileName = Path.Combine(arborDir, "deps_template.html"); 14 | var resultFileName = Path.Combine(arborDir, "deps.html"); 15 | 16 | if (!File.Exists(templateFileName)) 17 | { 18 | throw new CementException("deps_template.html not found"); 19 | } 20 | 21 | var text = File.ReadAllText(templateFileName); 22 | text = text.Replace( 23 | "", 24 | $""); 25 | text = text.Replace("$module_name", moduleName); 26 | 27 | File.WriteAllText(resultFileName, text); 28 | 29 | // dv.kab (20.06.2022): 30 | // https://docs.microsoft.com/en-us/dotnet/core/compatibility/fx-core#change-in-default-value-of-useshellexecute 31 | Process.Start(new ProcessStartInfo(resultFileName) {UseShellExecute = true}); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/GitHubAsset.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using JetBrains.Annotations; 3 | using Newtonsoft.Json; 4 | using Newtonsoft.Json.Converters; 5 | 6 | namespace Cement.Cli.Common; 7 | 8 | [JsonObject] 9 | [PublicAPI] 10 | public class GitHubAsset 11 | { 12 | [JsonProperty("browser_download_url")] 13 | public string BrowserDownloadUrl { get; set; } 14 | 15 | [JsonProperty("name")] 16 | public string Name { get; set; } 17 | 18 | [JsonProperty("label")] 19 | public string Label { get; set; } 20 | 21 | [JsonProperty("content_type")] 22 | public string ContentType { get; set; } 23 | 24 | [JsonProperty("created_at")] 25 | [JsonConverter(typeof(IsoDateTimeConverter))] 26 | public DateTimeOffset CreatedAt { get; set; } 27 | 28 | [JsonProperty("updated_at")] 29 | [JsonConverter(typeof(IsoDateTimeConverter))] 30 | public DateTimeOffset UpdatedAt { get; set; } 31 | 32 | public override string ToString() 33 | { 34 | return $"{nameof(BrowserDownloadUrl)}: '{BrowserDownloadUrl}', " + 35 | $"{nameof(Name)}: '{Name}', " + 36 | $"{nameof(Label)}: '{Label}', " + 37 | $"{nameof(ContentType)}: '{ContentType}', " + 38 | $"{nameof(CreatedAt)}: '{CreatedAt:O}', " + 39 | $"{nameof(UpdatedAt)}: '{UpdatedAt:O}'"; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/ModuleYamlFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace Cement.Cli.Common.YamlParsers; 7 | 8 | public sealed class ModuleYamlFile 9 | { 10 | public readonly List Lines; 11 | private readonly string lineEndings; 12 | 13 | public ModuleYamlFile(FileInfo moduleYamlPath) 14 | : this(File.ReadAllText(moduleYamlPath.FullName)) 15 | { 16 | } 17 | 18 | private ModuleYamlFile(string moduleYamlContent) 19 | { 20 | lineEndings = moduleYamlContent.Contains("\r\n") ? "\r\n" : "\n"; 21 | Lines = moduleYamlContent.Split(new[] {"\r\n", "\n"}, StringSplitOptions.None).ToList(); 22 | } 23 | 24 | public static void ReplaceTabs(string yamlPath) 25 | { 26 | var file = new ModuleYamlFile(new FileInfo(yamlPath)); 27 | for (var i = 0; i < file.Lines.Count; i++) 28 | file.Lines[i] = file.Lines[i].Replace("\t", " "); 29 | file.Save(yamlPath); 30 | } 31 | 32 | public void Save(string path, List newLines) 33 | { 34 | File.WriteAllText(path, string.Join(lineEndings, newLines)); 35 | } 36 | 37 | private void Save(string path) 38 | { 39 | File.WriteAllText(path, Lines.Aggregate((x, y) => x + lineEndings + y)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/Models/ModuleDefinition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using JetBrains.Annotations; 5 | 6 | namespace Cement.Cli.Common.YamlParsers.Models; 7 | 8 | public sealed class ModuleDefinition 9 | { 10 | private readonly ModuleConfig defaultConfig; 11 | 12 | public ModuleDefinition( 13 | [NotNull] IReadOnlyDictionary allConfigurations, 14 | [NotNull] ModuleDefaults defaults) 15 | { 16 | Defaults = defaults; 17 | AllConfigurations = allConfigurations; 18 | defaultConfig = allConfigurations.FirstOrDefault(kvp => kvp.Value.IsDefault).Value; 19 | } 20 | 21 | [NotNull] 22 | public IReadOnlyDictionary AllConfigurations { get; } 23 | 24 | [NotNull] 25 | public ModuleDefaults Defaults { get; } 26 | 27 | [CanBeNull] 28 | public ModuleConfig FindDefaultConfiguration() => defaultConfig; 29 | 30 | [NotNull] 31 | public ModuleConfig GetDefaultConfiguration() 32 | { 33 | if (defaultConfig == null) 34 | throw new ArgumentException("Cannot determine default module configuration. Specify it via '*default' keyword."); 35 | 36 | return defaultConfig; 37 | } 38 | 39 | public ModuleConfig this[string configName] => AllConfigurations[configName]; 40 | } 41 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/V2/ConfigSectionTitleParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Cement.Cli.Common.YamlParsers.Models; 4 | 5 | namespace Cement.Cli.Common.YamlParsers.V2; 6 | 7 | public sealed class ConfigSectionTitleParser 8 | { 9 | private const string DefaultKeyword = "*default"; 10 | private static readonly char[] Separators = {'>', ' ', ','}; 11 | 12 | public ConfigSectionTitle Parse(string title) 13 | { 14 | EnsureValidConfigLine(title); 15 | title = title.TrimEnd(); 16 | var isDefault = title.EndsWith(DefaultKeyword); 17 | if (isDefault) 18 | title = title.Substring(0, title.Length - DefaultKeyword.Length); 19 | 20 | EnsureValidConfigLine(title); 21 | 22 | var parts = title.Split(Separators, StringSplitOptions.RemoveEmptyEntries); 23 | var name = parts[0]; 24 | var parents = parts.Length > 1 ? parts.Skip(1).Distinct().ToArray() : null; 25 | 26 | return new ConfigSectionTitle 27 | { 28 | Name = name, 29 | IsDefault = isDefault, 30 | Parents = parents 31 | }; 32 | } 33 | 34 | private void EnsureValidConfigLine(string title) 35 | { 36 | if (string.IsNullOrWhiteSpace(title)) 37 | throw new ArgumentException("Got empty configuration line from module.yaml"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ReInstallCommand.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Commands.Attributes; 2 | using Cement.Cli.Commands.Extensions; 3 | using Cement.Cli.Common; 4 | using JetBrains.Annotations; 5 | 6 | namespace Cement.Cli.Commands; 7 | 8 | [PublicAPI] 9 | [HiddenCommand] 10 | public sealed class ReInstallCommand : Command 11 | { 12 | private readonly ConsoleWriter consoleWriter; 13 | private readonly ICommandActivator commandActivator; 14 | 15 | public ReInstallCommand(ConsoleWriter consoleWriter, ICommandActivator commandActivator) 16 | { 17 | this.consoleWriter = consoleWriter; 18 | this.commandActivator = commandActivator; 19 | } 20 | 21 | public override string Name => "reinstall"; 22 | public override string HelpMessage => @" 23 | Reinstalls cement 24 | NOTE: Don't use this command from installed cement. 25 | 26 | Usage: 27 | cm reinstall 28 | "; 29 | 30 | protected override int Execute(ReInstallCommandOptions options) 31 | { 32 | var selfUpdateCommand = commandActivator.Create(); 33 | selfUpdateCommand.IsInstallingCement = true; 34 | 35 | return selfUpdateCommand.Run(new[] {"self-update"}); 36 | } 37 | 38 | protected override ReInstallCommandOptions ParseArgs(string[] args) 39 | { 40 | return new ReInstallCommandOptions(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/RemovePackageCommand.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using Cement.Cli.Common; 4 | using Cement.Cli.Common.Exceptions; 5 | using JetBrains.Annotations; 6 | 7 | namespace Cement.Cli.Commands; 8 | 9 | [PublicAPI] 10 | public sealed class RemovePackageCommand : Command 11 | { 12 | public RemovePackageCommand(ConsoleWriter consoleWriter) 13 | { 14 | } 15 | 16 | public override string Name => "remove"; 17 | public override string HelpMessage => @"usage: cm packages remove "; 18 | 19 | protected override int Execute(RemovePackageCommandOptions options) 20 | { 21 | var settings = CementSettingsRepository.Get(); 22 | 23 | var package = settings.Packages.Find(p => p.Name.Equals(options.PackageName, StringComparison.Ordinal)); 24 | if (package == null) 25 | return 0; 26 | 27 | settings.Packages.Remove(package); 28 | 29 | CementSettingsRepository.Save(settings); 30 | return 0; 31 | } 32 | 33 | protected override RemovePackageCommandOptions ParseArgs(string[] args) 34 | { 35 | if (args.Length < 1) 36 | throw new BadArgumentException($"error: invalid arguments{Environment.NewLine}{HelpMessage}"); 37 | 38 | var packageName = args[0]; 39 | return new RemovePackageCommandOptions(packageName); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/TreeishManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Cement.Cli.Common.Exceptions; 4 | 5 | namespace Cement.Cli.Common; 6 | 7 | public sealed class TreeishManager 8 | { 9 | public bool TreeishAlreadyProcessed(Dep dep, IList processed) 10 | { 11 | return 12 | processed.Select(d => d.Treeish) 13 | .Any( 14 | dtreeish => 15 | (dtreeish == null && dep.Treeish == null) || 16 | (dtreeish != null && (dtreeish.Equals(dep.Treeish) || dep.Treeish == null))); 17 | } 18 | 19 | public void ThrowOnTreeishConflict(DepWithParent depWithParent, IList processed) 20 | { 21 | var conflictDep = 22 | processed.FirstOrDefault(d => d.Dep.Treeish != null && !d.Dep.Treeish.Equals(depWithParent.Dep.Treeish)); 23 | if (conflictDep != null && depWithParent.Dep.Treeish != null && conflictDep.Dep.Treeish != null && !conflictDep.Dep.Treeish.Trim().Equals("")) 24 | { 25 | throw new TreeishConflictException( 26 | string.Format( 27 | "Treeish conflict: {0} refers to {4}:{1}, while {2} refers to {4}:{3}", 28 | depWithParent.ParentModule, depWithParent.Dep.Treeish, conflictDep.ParentModule, conflictDep.Dep.Treeish, conflictDep.Dep.Name)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/DepsValidators/DepsValidator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Cement.Cli.Common.DepsValidators; 6 | 7 | public sealed class DepsValidator : IDepsValidator 8 | { 9 | private readonly string configurationName; 10 | 11 | public DepsValidator(string configurationName) 12 | { 13 | this.configurationName = configurationName; 14 | } 15 | 16 | public DepsValidateResult Validate(IEnumerable deps, out IReadOnlyCollection validateErrors) 17 | { 18 | var duplicatedDeps = new HashSet(StringComparer.OrdinalIgnoreCase); 19 | var uniqueDeps = new HashSet(StringComparer.OrdinalIgnoreCase); 20 | 21 | foreach (var dep in deps) 22 | { 23 | if (uniqueDeps.Contains(dep.Name)) 24 | { 25 | duplicatedDeps.Add(dep.Name); 26 | continue; 27 | } 28 | 29 | uniqueDeps.Add(dep.Name); 30 | } 31 | 32 | if (duplicatedDeps.Count == 0) 33 | { 34 | validateErrors = Array.Empty(); 35 | return DepsValidateResult.Valid; 36 | } 37 | 38 | validateErrors = duplicatedDeps 39 | .Select(dep => $"The dependency '{dep}' is declared several times within the '{configurationName}' configuration") 40 | .ToArray(); 41 | 42 | return DepsValidateResult.Invalid; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Module.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Cement.Cli.Common.Exceptions; 3 | 4 | namespace Cement.Cli.Common; 5 | 6 | public sealed class Module 7 | { 8 | public Module(string name, string url, string pushurl) 9 | { 10 | Name = name; 11 | Url = url; 12 | Pushurl = pushurl; 13 | } 14 | 15 | public Module(IniData parsedData, string sectionName) 16 | { 17 | var sectionNameTokens = sectionName.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); 18 | if (sectionNameTokens.Length < 2 || !sectionNameTokens[0].Equals("module")) 19 | { 20 | throw new CementException("Section is not module: " + sectionName); 21 | } 22 | 23 | Name = sectionNameTokens[1]; 24 | Type = parsedData.GetValue("type", sectionName).Equals("") 25 | ? "git" 26 | : parsedData.GetValue("type", sectionName); 27 | if (parsedData.GetValue("url", sectionName).Equals("")) 28 | { 29 | throw new CementException("Missing url in module: " + sectionName); 30 | } 31 | 32 | Url = parsedData.GetValue("url", sectionName); 33 | Pushurl = parsedData.GetValue("pushurl", sectionName).Equals("") 34 | ? null 35 | : parsedData.GetValue("pushurl", sectionName); 36 | } 37 | 38 | public string Name { get; } 39 | public string Url { get; } 40 | public string Pushurl { get; } 41 | public string Type { get; } 42 | } 43 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/JsonConverters/DepJsonConverter.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using System.Text; 4 | using Newtonsoft.Json; 5 | 6 | namespace Cement.Cli.Common.JsonConverters; 7 | 8 | internal sealed class DepJsonConverter : JsonConverter 9 | { 10 | public override bool CanConvert(Type objectType) 11 | { 12 | return objectType == typeof(Dep); 13 | } 14 | 15 | public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) 16 | { 17 | var str = reader.Value?.ToString(); 18 | return new Dep(str); 19 | } 20 | 21 | public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) 22 | { 23 | if (value is not Dep item) 24 | return; 25 | 26 | writer.WriteValue(ToJsonValueString(item)); 27 | writer.Flush(); 28 | } 29 | 30 | private static string ToJsonValueString(Dep source) 31 | { 32 | var result = new StringBuilder(source.Name); 33 | 34 | if (source.Configuration != null) 35 | result.Append('/').Append(EscapeBadChars(source.Configuration)); 36 | 37 | if (source.Treeish != null) 38 | result.Append('@').Append(EscapeBadChars(source.Treeish)); 39 | 40 | return result.ToString(); 41 | } 42 | 43 | private static string EscapeBadChars(string str) 44 | { 45 | return str.Replace("@", "\\@").Replace("/", "\\/"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/HooksYamlParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Cement.Cli.Common.Exceptions; 6 | 7 | namespace Cement.Cli.Common.YamlParsers; 8 | 9 | public sealed class HooksYamlParser : ConfigurationYamlParser 10 | { 11 | private const string SectionName = "hooks"; 12 | 13 | public HooksYamlParser(FileInfo moduleName) 14 | : base(moduleName) 15 | { 16 | } 17 | 18 | public HooksYamlParser(string moduleName, string text) 19 | : base(moduleName, text) 20 | { 21 | } 22 | 23 | public List Get() 24 | { 25 | try 26 | { 27 | var configDict = GetConfigurationSection("default"); 28 | return GetHooksFromSection(configDict); 29 | } 30 | catch (BadYamlException) 31 | { 32 | throw; 33 | } 34 | catch (Exception exception) 35 | { 36 | throw new BadYamlException(ModuleName, SectionName, exception.Message); 37 | } 38 | } 39 | 40 | private static List GetHooksFromSection(Dictionary configDict) 41 | { 42 | if (configDict == null || !configDict.ContainsKey(SectionName)) 43 | return new List(); 44 | if (configDict[SectionName] is not List hooks) 45 | return new List(); 46 | 47 | return hooks.Select(h => h.ToString()).ToList(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/FixReferenceResultPrinter.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common; 3 | 4 | namespace Cement.Cli.Commands; 5 | 6 | internal sealed class FixReferenceResultPrinter 7 | { 8 | private readonly ConsoleWriter consoleWriter; 9 | 10 | public FixReferenceResultPrinter(ConsoleWriter consoleWriter) 11 | { 12 | this.consoleWriter = consoleWriter; 13 | } 14 | 15 | public void Print(FixReferenceResult result) 16 | { 17 | if (result.NoYamlModules.Any()) 18 | { 19 | consoleWriter.WriteWarning("No 'install' section in modules:"); 20 | foreach (var m in result.NoYamlModules) 21 | consoleWriter.WriteBuildWarning("\t- " + m); 22 | } 23 | 24 | foreach (var key in result.Replaced.Keys) 25 | { 26 | if (!result.Replaced[key].Any()) 27 | continue; 28 | consoleWriter.WriteOk(key + " replaces:"); 29 | foreach (var value in result.Replaced[key]) 30 | consoleWriter.WriteLine("\t" + value); 31 | } 32 | 33 | foreach (var key in result.NotFound.Keys) 34 | { 35 | if (!result.NotFound[key].Any()) 36 | continue; 37 | consoleWriter.WriteError(key + "\n\tnot found references in install/artifacts section of any module:"); 38 | foreach (var value in result.NotFound[key]) 39 | consoleWriter.WriteLine("\t" + value); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/ParsersTests/TestIniParser.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using NUnit.Framework; 3 | 4 | namespace Cement.Cli.Tests.ParsersTests; 5 | 6 | [TestFixture] 7 | internal class TestIniParser 8 | { 9 | [Test] 10 | public void TestOneSectionsManyValues() 11 | { 12 | const string txt = @"[package localpackage] 13 | url = C:\temp 14 | type = file 15 | a = b 16 | "; 17 | var parsed = new IniParser().ParseString(txt); 18 | var sections = parsed.GetSections(); 19 | Assert.AreEqual(new[] {"package localpackage"}, sections); 20 | var options = parsed.GetKeys(sections[0]); 21 | Assert.AreEqual(new[] {"url", "type", "a"}, options); 22 | } 23 | 24 | [Test] 25 | public void TestManySections() 26 | { 27 | const string txt = @"[package s1] 28 | url = C:\temp\A\ 29 | 30 | [package s2] 31 | url = C:\temp\B\ 32 | 33 | [package s3] 34 | url = C:\temp\C\ 35 | 36 | "; 37 | var parsed = new IniParser().ParseString(txt); 38 | var sections = parsed.GetSections(); 39 | Assert.AreEqual(new[] {"package s1", "package s2", "package s3"}, sections); 40 | } 41 | 42 | [Test] 43 | public void TestMultiline() 44 | { 45 | const string txt = @" 46 | [module A] 47 | multiline = 48 | Q 49 | W 50 | E"; 51 | var parsed = new IniParser().ParseString(txt); 52 | var ans = parsed.GetValue("multiline", "module A"); 53 | Assert.AreEqual("\r\nQ\r\nW\r\nE", ans); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/ConfigurationParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using Cement.Cli.Common.YamlParsers; 4 | 5 | namespace Cement.Cli.Common; 6 | 7 | public sealed class ConfigurationParser : IConfigurationParser 8 | { 9 | private readonly IConfigurationParser parser; 10 | 11 | public ConfigurationParser(FileInfo modulePath) 12 | { 13 | if (File.Exists(Path.Combine(modulePath.FullName, Helper.YamlSpecFile))) 14 | parser = new ConfigurationYamlParser(modulePath); 15 | } 16 | 17 | public IList GetConfigurations() 18 | { 19 | return parser == null ? new[] {"full-build"} : parser.GetConfigurations(); 20 | } 21 | 22 | public bool ConfigurationExists(string configName) 23 | { 24 | return parser == null ? configName.Equals("full-build") : parser.ConfigurationExists(configName); 25 | } 26 | 27 | public string GetDefaultConfigurationName() 28 | { 29 | return parser == null ? null : parser.GetDefaultConfigurationName(); 30 | } 31 | 32 | public IList GetParentConfigurations(string configName) 33 | { 34 | return parser == null ? new List() : parser.GetParentConfigurations(configName); 35 | } 36 | 37 | public Dictionary> GetConfigurationsHierarchy() 38 | { 39 | return parser == null ? new Dictionary> {{"full-build", new List()}} : parser.GetConfigurationsHierarchy(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/ParsersTests/TestModuleIniParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common; 3 | using NUnit.Framework; 4 | 5 | namespace Cement.Cli.Tests.ParsersTests; 6 | 7 | [TestFixture] 8 | public class TestModuleIniParser 9 | { 10 | [Test] 11 | public void TestSampleModuleConfigWithComment() 12 | { 13 | var txt = @"[module localmodule] 14 | ;some comment 15 | url = C:\temp\modules 16 | pushurl = C:\temp\modules 17 | [module abc] 18 | url = C:\temp\abc\"; 19 | var result = ModuleIniParser.Parse(txt); 20 | Assert.AreEqual(new[] {"localmodule", "abc"}, result.Select(p => p.Name).ToArray()); 21 | } 22 | 23 | [Test] 24 | public void TestSkipsNonModuleSections() 25 | { 26 | var txt = @"[module local] 27 | url = C:\temp\modules 28 | type = file 29 | [group abc] 30 | modules = 31 | a 32 | b 33 | c 34 | [module m] 35 | url = C:\temp\abc\"; 36 | var result = ModuleIniParser.Parse(txt); 37 | Assert.AreEqual(new[] {"local", "m"}, result.Select(m => m.Name)); 38 | } 39 | 40 | [Test] 41 | public void TestSkipsModuleWithoutUrl() 42 | { 43 | var txt = @"[module withoutUrl] 44 | type = git"; 45 | Assert.AreEqual(0, ModuleIniParser.Parse(txt).Length); 46 | } 47 | 48 | [Test] 49 | public void TestDefaultTypeIsGit() 50 | { 51 | var txt = @"[module local] 52 | url = C:\temp\modules"; 53 | var type = ModuleIniParser.Parse(txt)[0].Type; 54 | Assert.AreEqual("git", type); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/UpdateCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common; 3 | using Cement.Cli.Common.Exceptions; 4 | using NDesk.Options; 5 | 6 | namespace Cement.Cli.Commands.OptionsParsers; 7 | 8 | public sealed class UpdateCommandOptionsParser : OptionsParser 9 | { 10 | public override UpdateCommandOptions Parse(string[] args) 11 | { 12 | string treeish = null; 13 | var reset = false; 14 | var force = false; 15 | var pullAnyway = false; 16 | var verbose = false; 17 | int? gitDepth = null; 18 | 19 | var parser = new OptionSet 20 | { 21 | {"r|reset", _ => reset = true}, 22 | {"p|pull-anyway", _ => pullAnyway = true}, 23 | {"f|force", _ => force = true}, 24 | {"v|verbose", _ => verbose = true}, 25 | {"git-depth=", d => gitDepth = int.Parse(d)} 26 | }; 27 | var extraArgs = parser.Parse(args.Skip(1)); 28 | if (extraArgs.Count > 0) 29 | { 30 | treeish = extraArgs[0]; 31 | ThrowIfHasExtraArgs(extraArgs.Skip(1).ToList()); 32 | } 33 | 34 | if ((force && reset) || (force && pullAnyway) || (reset && pullAnyway)) 35 | { 36 | throw new BadArgumentException(); 37 | } 38 | 39 | var policy = PolicyMapper.GetLocalChangesPolicy(force, reset, pullAnyway); 40 | return new UpdateCommandOptions(treeish, verbose, policy, gitDepth); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Benchmarks/Benchmarks/DepLineParserBenchmark.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using BenchmarkDotNet.Columns; 3 | using BenchmarkDotNet.Configs; 4 | using Cement.Cli.Common; 5 | using Cement.Cli.Common.YamlParsers.Models; 6 | using Cement.Cli.Common.YamlParsers.V2; 7 | using JetBrains.Annotations; 8 | 9 | namespace Cement.Cli.Benchmarks.Benchmarks; 10 | 11 | [PublicAPI] 12 | [Config(typeof(Config))] 13 | public class DepLineParserBenchmark 14 | { 15 | [ParamsSource(nameof(DepLines))] 16 | public string? DepLine { get; set; } 17 | private DepSectionItemParser? parser; 18 | 19 | public IEnumerable DepLines => new[] 20 | { 21 | "module", 22 | "module@b32742e9701aef44ee986db2824e9007056ba60f/some-cfg", 23 | "module/some-cfg@b32742e9701aef44ee986db2824e9007056ba60f", 24 | // ReSharper disable once StringLiteralTypo 25 | @"module@hello\/there\@general/kenobi" 26 | }; 27 | 28 | [GlobalSetup] 29 | public void Setup() 30 | { 31 | parser = new DepSectionItemParser(); 32 | } 33 | 34 | [Benchmark] 35 | public DepSectionItem Parse1() => parser!.Parse(DepLine); 36 | 37 | [Benchmark] 38 | public Dep Parse2() => new(DepLine); 39 | 40 | private sealed class Config : ManualConfig 41 | { 42 | public Config() 43 | { 44 | AddColumn( 45 | StatisticColumn.P85, 46 | StatisticColumn.P90, 47 | StatisticColumn.P95, 48 | StatisticColumn.P100); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Extensions/StreamExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Buffers; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace Cement.Cli.Common.Extensions; 7 | 8 | internal static class StreamExtensions 9 | { 10 | public static byte[] ReadAllBytes(this Stream stream) 11 | { 12 | using var buffer = new MemoryStream(); 13 | stream.CopyTo(buffer); 14 | 15 | return buffer.ToArray(); 16 | } 17 | 18 | public static byte[] ReadAllBytesWithProgress(this Stream source, Action reportProgress) 19 | { 20 | using var destination = new MemoryStream(); 21 | using var streamReader = new StreamReader(source, Encoding.UTF8); 22 | 23 | // This value was originally picked to be the largest multiple of 4096 that is still smaller than 24 | // the large object heap threshold (85K). 25 | const int readBufferSize = 4096 * 20; 26 | var readBuffer = ArrayPool.Shared.Rent(readBufferSize); 27 | 28 | var totalBytesRead = 0L; 29 | 30 | try 31 | { 32 | int bytesRead; 33 | while ((bytesRead = source.Read(readBuffer, 0, readBuffer.Length)) != 0) 34 | { 35 | destination.Write(readBuffer, 0, bytesRead); 36 | totalBytesRead += bytesRead; 37 | 38 | reportProgress(totalBytesRead); 39 | } 40 | } 41 | finally 42 | { 43 | ArrayPool.Shared.Return(readBuffer); 44 | } 45 | 46 | return destination.ToArray(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/V2/Factories/ModuleYamlParserFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Cement.Cli.Common.YamlParsers.V2.Factories; 4 | 5 | public static class ModuleYamlParserFactory 6 | { 7 | private static readonly Lazy Instance = new(Create); 8 | 9 | public static ModuleYamlParser Get() 10 | { 11 | return Instance.Value; 12 | } 13 | 14 | private static ModuleYamlParser Create() 15 | { 16 | var configSectionTitleParser = new ConfigSectionTitleParser(); 17 | var depLineParser = new DepSectionItemParser(); 18 | var depsSectionParser = new DepsSectionParser(depLineParser); 19 | var installSectionParser = new InstallSectionParser(); 20 | var buildSectionParser = new BuildSectionParser(); 21 | var configSectionParser = new ConfigSectionParser(configSectionTitleParser, installSectionParser, depsSectionParser, buildSectionParser); 22 | 23 | var hooksSectionParser = new HooksSectionParser(); 24 | var settingsSectionParser = new SettingsSectionParser(); 25 | var moduleDefaultsParser = new ModuleDefaultsParser(hooksSectionParser, depsSectionParser, settingsSectionParser, buildSectionParser, installSectionParser); 26 | 27 | var depsSectionMerger = new DepsSectionMerger(); 28 | var installSectionMerger = new InstallSectionMerger(); 29 | 30 | return new ModuleYamlParser( 31 | moduleDefaultsParser, 32 | configSectionParser, 33 | installSectionMerger, 34 | depsSectionMerger 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/V2/ConfigurationHierarchy.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using JetBrains.Annotations; 3 | 4 | namespace Cement.Cli.Common.YamlParsers.V2; 5 | 6 | public sealed class ConfigurationHierarchy 7 | { 8 | [CanBeNull] 9 | private readonly string defaultConfig; 10 | [NotNull] 11 | private readonly string[] allConfigsSorted; 12 | [NotNull] 13 | private readonly IReadOnlyDictionary configNameToAllParentsMap; 14 | 15 | public ConfigurationHierarchy( 16 | [NotNull] string[] allConfigsSorted, 17 | [NotNull] IReadOnlyDictionary configNameToAllParentsMap, 18 | [CanBeNull] string defaultConfig = null) 19 | { 20 | this.defaultConfig = defaultConfig; 21 | this.allConfigsSorted = allConfigsSorted; 22 | this.configNameToAllParentsMap = configNameToAllParentsMap; 23 | } 24 | 25 | /// 26 | /// Return all ancestors of a given configuration in order: 27 | /// From smallest config with no deps to complex configs with multiple ancestors. 28 | /// 29 | public string[] GetAllParents(string configName) => configNameToAllParentsMap[configName]; 30 | 31 | /// 32 | /// Return default configuration name 33 | /// 34 | public string FindDefault() => defaultConfig; 35 | 36 | /// 37 | /// Return all configurations in order: 38 | /// From smallest config with no deps to complex configs with multiple ancestors. 39 | /// 40 | public string[] GetAll() => allConfigsSorted; 41 | } 42 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Extensions/InstallDataExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using JetBrains.Annotations; 5 | 6 | namespace Cement.Cli.Common.Extensions; 7 | 8 | public static class InstallDataExtensions 9 | { 10 | /// 11 | /// Returns new instance of InstallData with properties copied from first and second InstallData 12 | /// 13 | public static InstallData JoinWith(this InstallData first, [NotNull] InstallData second, IEnumerable currentConfigurationInstallFiles) 14 | { 15 | var result = new InstallData 16 | { 17 | Artifacts = new List(ConcatDistinct(first, second, item => item.Artifacts)), 18 | ExternalModules = new List(ConcatDistinct(first, second, item => item.ExternalModules)), 19 | InstallFiles = new List(ConcatDistinct(first, second, item => item.InstallFiles)), 20 | NuGetPackages = new List(ConcatDistinct(first, second, item => item.NuGetPackages)), 21 | CurrentConfigurationInstallFiles = new List(currentConfigurationInstallFiles) 22 | }; 23 | 24 | return result; 25 | } 26 | 27 | private static IEnumerable ConcatDistinct(InstallData a, InstallData b, Func> getCollectionFunc) 28 | { 29 | var firstCollection = getCollectionFunc(a) ?? Enumerable.Empty(); 30 | var secondCollection = getCollectionFunc(b) ?? Enumerable.Empty(); 31 | 32 | return firstCollection.Union(secondCollection); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/RefAddCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using Cement.Cli.Common.Exceptions; 3 | using NDesk.Options; 4 | 5 | namespace Cement.Cli.Commands.OptionsParsers; 6 | 7 | public sealed class RefAddCommandOptionsParser : OptionsParser 8 | { 9 | public override RefAddCommandOptions Parse(string[] args) 10 | { 11 | string configuration = null; 12 | var testReplaces = false; 13 | var force = false; 14 | 15 | var parser = new OptionSet 16 | { 17 | {"c|configuration=", conf => configuration = conf}, 18 | {"testReplaces", _ => testReplaces = true}, 19 | {"force", _ => force = true} 20 | }; 21 | 22 | args = parser.Parse(args).ToArray(); 23 | if (args.Length != 4 || args[0] != "ref" || args[1] != "add") 24 | { 25 | throw new BadArgumentException( 26 | "Wrong usage of command.\n" + 27 | "Usage: cm ref add [/configuration] "); 28 | } 29 | 30 | var module = args[2]; 31 | var project = args[3]; 32 | 33 | var dep = new Dep(module); 34 | if (configuration != null) 35 | dep.Configuration = configuration; 36 | 37 | // todo(dstarasov): кажется, это не ответственность парсера, а скорее какого-то валидатора или самой команды 38 | if (!project.EndsWith(".csproj")) 39 | throw new BadArgumentException(project + " is not csproj file"); 40 | 41 | return new RefAddCommandOptions(project, dep, testReplaces, force); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/GitHubRelease.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using JetBrains.Annotations; 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Converters; 6 | 7 | namespace Cement.Cli.Common; 8 | 9 | [JsonObject] 10 | [PublicAPI] 11 | public class GitHubRelease 12 | { 13 | [JsonProperty("id")] 14 | public int Id { get; set; } 15 | 16 | [JsonProperty("name")] 17 | public string Name { get; set; } 18 | 19 | [JsonProperty("tag_name")] 20 | public string TagName { get; set; } 21 | 22 | [JsonProperty("target_commitish")] 23 | public string TargetCommitsh { get; set; } 24 | 25 | [JsonProperty("prerelease")] 26 | public bool Prerelease { get; set; } 27 | 28 | [JsonProperty("created_at")] 29 | [JsonConverter(typeof(IsoDateTimeConverter))] 30 | public DateTimeOffset CreatedAt { get; set; } 31 | 32 | [JsonProperty("published_at")] 33 | [JsonConverter(typeof(IsoDateTimeConverter))] 34 | public DateTimeOffset PublishedAt { get; set; } 35 | 36 | [JsonProperty("assets")] 37 | public IReadOnlyList Assets { get; set; } 38 | 39 | public override string ToString() 40 | { 41 | return $"{nameof(Id)}: {Id}, " + 42 | $"{nameof(Name)}: '{Name}', " + 43 | $"{nameof(TagName)}: '{TagName}', " + 44 | $"{nameof(TargetCommitsh)}: '{TargetCommitsh}', " + 45 | $"{nameof(Prerelease)}: {Prerelease}, " + 46 | $"{nameof(CreatedAt)}: '{CreatedAt:O}', " + 47 | $"{nameof(PublishedAt)}: '{PublishedAt:O}', " + 48 | $"{nameof(Assets)}: [{Assets}]"; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/GitRepositoryFactory.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Cement.Cli.Common.Logging; 3 | using JetBrains.Annotations; 4 | 5 | namespace Cement.Cli.Common; 6 | 7 | [PublicAPI] 8 | public sealed class GitRepositoryFactory : IGitRepositoryFactory 9 | { 10 | private readonly ConsoleWriter consoleWriter; 11 | private readonly BuildHelper buildHelper; 12 | 13 | public GitRepositoryFactory(ConsoleWriter consoleWriter, BuildHelper buildHelper) 14 | { 15 | this.consoleWriter = consoleWriter; 16 | this.buildHelper = buildHelper; 17 | } 18 | 19 | public GitRepository Create(string moduleName, string workspace) 20 | { 21 | var shellRunnerLogger = LogManager.GetLogger(); 22 | var shellRunner = new ShellRunner(shellRunnerLogger); 23 | 24 | var repoPath = Path.Combine(workspace, moduleName); 25 | 26 | var gitRepositoryLogger = LogManager.GetLogger(); 27 | return new GitRepository(gitRepositoryLogger, consoleWriter, buildHelper, shellRunner, repoPath, moduleName, workspace); 28 | } 29 | 30 | public GitRepository Create(string repoPath) 31 | { 32 | var shellRunnerLogger = LogManager.GetLogger(); 33 | var shellRunner = new ShellRunner(shellRunnerLogger); 34 | 35 | var moduleName = Path.GetFileName(repoPath); 36 | var workspace = Directory.GetParent(repoPath)!.FullName; 37 | 38 | var gitRepositoryLogger = LogManager.GetLogger(); 39 | return new GitRepository(gitRepositoryLogger, consoleWriter, buildHelper, shellRunner, repoPath, moduleName, workspace); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/AddPackageCommand.cs: -------------------------------------------------------------------------------- 1 | #nullable enable 2 | using System; 3 | using Cement.Cli.Common; 4 | using Cement.Cli.Common.Exceptions; 5 | using JetBrains.Annotations; 6 | 7 | namespace Cement.Cli.Commands; 8 | 9 | [PublicAPI] 10 | public sealed class AddPackageCommand : Command 11 | { 12 | public override string Name => "add"; 13 | public override string HelpMessage => @"usage: cm packages add "; 14 | 15 | protected override int Execute(AddPackageCommandOptions options) 16 | { 17 | var settings = CementSettingsRepository.Get(); 18 | var packageName = options.PackageName; 19 | var packageUrl = options.PackageUrl; 20 | 21 | var package = settings.Packages.Find(p => p.Name.Equals(packageName, StringComparison.Ordinal)); 22 | if (package != null) 23 | { 24 | if (package.Url.Equals(packageUrl)) 25 | return 0; 26 | 27 | throw new CementException($"error: conflict: package '{packageName}' already exists"); 28 | } 29 | 30 | package = new Package(packageName, packageUrl); 31 | settings.Packages.Add(package); 32 | 33 | CementSettingsRepository.Save(settings); 34 | return 0; 35 | } 36 | 37 | protected override AddPackageCommandOptions ParseArgs(string[] args) 38 | { 39 | if (args.Length < 2) 40 | throw new BadArgumentException($"error: invalid arguments{Environment.NewLine}{HelpMessage}"); 41 | 42 | var packageName = args[0]; 43 | var packageUrl = args[1]; 44 | 45 | return new AddPackageCommandOptions(packageName, packageUrl); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/InstallXmlParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Xml.Linq; 4 | using Cement.Cli.Common.Exceptions; 5 | 6 | namespace Cement.Cli.Common; 7 | 8 | [Obsolete("This component is deprecated and will be removed soon")] 9 | public sealed class InstallXmlParser 10 | { 11 | private readonly XDocument document; 12 | private readonly string moduleName; 13 | 14 | public InstallXmlParser(string xmlContent, string moduleName) 15 | { 16 | this.moduleName = moduleName; 17 | document = XDocument.Parse(xmlContent); 18 | } 19 | 20 | public InstallData Get(string configuration = null) 21 | { 22 | var result = new InstallData(); 23 | var configs = document.Descendants("install"); 24 | 25 | if (configuration == null) 26 | { 27 | var defaultInstall = configs.FirstOrDefault(c => !c.HasAttributes); 28 | if (defaultInstall == null) 29 | throw new NoSuchConfigurationException(moduleName, "default"); 30 | result.CurrentConfigurationInstallFiles = defaultInstall.Elements("add-ref") 31 | .Select(e => e.FirstAttribute.Value).ToList(); 32 | return result; 33 | } 34 | 35 | var config = configs.FirstOrDefault(c => c.Attribute("target") != null && c.Attribute("target").Value == configuration); 36 | if (config == null) 37 | { 38 | throw new NoSuchConfigurationException(moduleName, configuration); 39 | } 40 | 41 | result.CurrentConfigurationInstallFiles = config.Elements("add-ref") 42 | .Select(e => e.FirstAttribute.Value).ToList(); 43 | return result; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/BuildCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common; 3 | using Cement.Cli.Common.Exceptions; 4 | using NDesk.Options; 5 | 6 | namespace Cement.Cli.Commands.OptionsParsers; 7 | 8 | public sealed class BuildCommandOptionsParser : OptionsParser 9 | { 10 | public override BuildCommandOptions Parse(string[] args) 11 | { 12 | string configuration = null; 13 | var warnings = false; 14 | var obsolete = false; 15 | var verbose = false; 16 | var progress = false; 17 | var cleanBeforeBuild = false; 18 | 19 | var parser = new OptionSet 20 | { 21 | {"c|configuration=", conf => configuration = conf}, 22 | {"w|warnings", _ => warnings = true}, 23 | {"v|verbose", _ => verbose = true}, 24 | {"p|progress", _ => progress = true}, 25 | {"cleanBeforeBuild", _ => cleanBeforeBuild = true}, 26 | {"W", _ => obsolete = true}, 27 | }; 28 | var extraArgs = parser.Parse(args.Skip(1)); 29 | ThrowIfHasExtraArgs(extraArgs); 30 | 31 | if (verbose && (warnings || progress)) 32 | { 33 | throw new BadArgumentException(); 34 | } 35 | 36 | var buildSettings = new BuildSettings 37 | { 38 | ShowAllWarnings = warnings, 39 | ShowObsoleteWarnings = obsolete, 40 | ShowOutput = verbose, 41 | ShowProgress = progress, 42 | ShowWarningsSummary = true, 43 | CleanBeforeBuild = cleanBeforeBuild 44 | }; 45 | 46 | return new BuildCommandOptions(configuration, buildSettings); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/BuildDepsCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common; 3 | using Cement.Cli.Common.Exceptions; 4 | using NDesk.Options; 5 | 6 | namespace Cement.Cli.Commands.OptionsParsers; 7 | 8 | public sealed class BuildDepsCommandOptionsParser : OptionsParser 9 | { 10 | public override BuildDepsCommandOptions Parse(string[] args) 11 | { 12 | var rebuild = false; 13 | string configuration = null; 14 | var warnings = false; 15 | var verbose = false; 16 | var progress = false; 17 | var quickly = false; 18 | var cleanBeforeBuild = false; 19 | 20 | var parser = new OptionSet 21 | { 22 | {"r|rebuild", _ => rebuild = true}, 23 | {"c|configuration=", conf => configuration = conf}, 24 | {"w|warnings", _ => warnings = true}, 25 | {"v|verbose", _ => verbose = true}, 26 | {"p|progress", _ => progress = true}, 27 | {"q|quickly", _ => quickly = true}, 28 | {"cleanBeforeBuild", _ => cleanBeforeBuild = true} 29 | }; 30 | var extraArgs = parser.Parse(args.Skip(1)); 31 | ThrowIfHasExtraArgs(extraArgs); 32 | 33 | if (verbose && (warnings || progress)) 34 | { 35 | throw new BadArgumentException(); 36 | } 37 | 38 | var buildSettings = new BuildSettings 39 | { 40 | ShowAllWarnings = warnings, 41 | ShowOutput = verbose, 42 | ShowProgress = progress, 43 | CleanBeforeBuild = cleanBeforeBuild 44 | }; 45 | return new BuildDepsCommandOptions(configuration, rebuild, quickly, buildSettings); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/UpdateDepsCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common; 3 | using Cement.Cli.Common.Exceptions; 4 | using NDesk.Options; 5 | 6 | namespace Cement.Cli.Commands.OptionsParsers; 7 | 8 | public sealed class UpdateDepsCommandOptionsParser : OptionsParser 9 | { 10 | public override UpdateDepsCommandOptions Parse(string[] args) 11 | { 12 | var reset = false; 13 | var force = false; 14 | var pullAnyway = false; 15 | string configuration = null; 16 | string merged = null; 17 | var localBranchForce = false; 18 | var verbose = false; 19 | int? gitDepth = null; 20 | 21 | var parser = new OptionSet 22 | { 23 | {"r|reset", _ => reset = true}, 24 | {"p|pull-anyway", _ => pullAnyway = true}, 25 | {"c|configuration=", conf => configuration = conf}, 26 | {"f|force", _ => force = true}, 27 | {"m|merged:", m => merged = m ?? "master"}, 28 | {"allow-local-branch-force", _ => localBranchForce = true}, 29 | {"v|verbose", _ => verbose = true}, 30 | {"git-depth=", d => gitDepth = int.Parse(d)} 31 | }; 32 | 33 | var extraArgs = parser.Parse(args.Skip(1)); 34 | ThrowIfHasExtraArgs(extraArgs); 35 | 36 | if ((force && reset) || (force && pullAnyway) || (reset && pullAnyway)) 37 | { 38 | throw new BadArgumentException(); 39 | } 40 | 41 | var policy = PolicyMapper.GetLocalChangesPolicy(force, reset, pullAnyway); 42 | return new UpdateDepsCommandOptions(configuration, merged, policy, localBranchForce, verbose, gitDepth); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/CommandHelper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Cement.Cli.Common; 3 | using Cement.Cli.Common.Exceptions; 4 | 5 | namespace Cement.Cli.Commands; 6 | 7 | public static class CommandHelper 8 | { 9 | public static void SetWorkspace(CommandLocation commandLocation) 10 | { 11 | var cwd = Directory.GetCurrentDirectory(); 12 | if (commandLocation == CommandLocation.WorkspaceDirectory) 13 | { 14 | if (!Helper.IsCementTrackedDirectory(cwd)) 15 | throw new CementTrackException(cwd + " is not cement workspace directory."); 16 | Helper.SetWorkspace(cwd); 17 | } 18 | 19 | if (commandLocation == CommandLocation.RootModuleDirectory) 20 | { 21 | if (!Helper.IsCurrentDirectoryModule(cwd)) 22 | throw new CementTrackException(cwd + " is not cement module directory."); 23 | Helper.SetWorkspace(Directory.GetParent(cwd).FullName); 24 | } 25 | 26 | if (commandLocation == CommandLocation.InsideModuleDirectory) 27 | { 28 | var currentModuleDirectory = Helper.GetModuleDirectory(Directory.GetCurrentDirectory()); 29 | if (currentModuleDirectory == null) 30 | throw new CementTrackException("Can't locate module directory"); 31 | Helper.SetWorkspace(Directory.GetParent(currentModuleDirectory).FullName); 32 | } 33 | } 34 | 35 | public static void CheckRequireYaml(CommandLocation commandLocation, bool requireModuleYaml) 36 | { 37 | if (commandLocation == CommandLocation.RootModuleDirectory && requireModuleYaml && !File.Exists(Helper.YamlSpecFile)) 38 | throw new CementException("No " + Helper.YamlSpecFile + " file found"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Cement.Cli.Common.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net6.0 4 | Common 5 | Copyright © 2022 6 | warnings 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ../../externals/NuGet.dll 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/SelfUpdate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Cement.Cli.Common; 3 | using Cement.Cli.Common.Logging; 4 | using JetBrains.Annotations; 5 | using Microsoft.Extensions.Logging; 6 | 7 | namespace Cement.Cli.Commands; 8 | 9 | [PublicAPI] 10 | public static class SelfUpdate 11 | { 12 | public static void UpdateIfOld(ConsoleWriter consoleWriter) 13 | { 14 | var log = LogManager.GetLogger(nameof(SelfUpdate)); 15 | 16 | try 17 | { 18 | var isEnabledSelfUpdate = CementSettingsRepository.Get().IsEnabledSelfUpdate; 19 | if (isEnabledSelfUpdate.HasValue && !isEnabledSelfUpdate.Value) 20 | return; 21 | var lastUpdate = Helper.GetLastUpdateTime(); 22 | var now = DateTime.Now; 23 | var diff = now - lastUpdate; 24 | if (diff <= TimeSpan.FromHours(5)) 25 | return; 26 | 27 | var logger = LogManager.GetLogger(); 28 | var selfUpdateCommand = new SelfUpdateCommand(logger, consoleWriter) 29 | { 30 | IsAutoUpdate = true 31 | }; 32 | 33 | var exitCode = selfUpdateCommand.Run(new[] {"self-update"}); 34 | if (exitCode != 0) 35 | { 36 | log.LogError("Auto update cement failed. 'self-update' exited with code '{Code}'", exitCode); 37 | consoleWriter.WriteWarning("Auto update failed. Check previous warnings for details"); 38 | } 39 | } 40 | catch (Exception exception) 41 | { 42 | log.LogError(exception, "Auto update failed, error: '{ErrorMessage}'", exception.Message); 43 | consoleWriter.WriteWarning("Auto update failed. Check logs for details"); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/InitCommand.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Cement.Cli.Common; 3 | using JetBrains.Annotations; 4 | 5 | namespace Cement.Cli.Commands; 6 | 7 | [PublicAPI] 8 | public sealed class InitCommand : ICommand 9 | { 10 | private readonly ConsoleWriter consoleWriter; 11 | 12 | public InitCommand(ConsoleWriter consoleWriter) 13 | { 14 | this.consoleWriter = consoleWriter; 15 | } 16 | 17 | public bool MeasureElapsedTime { get; } 18 | 19 | public bool RequireModuleYaml { get; } 20 | 21 | public CommandLocation Location { get; } = CommandLocation.Any; 22 | 23 | public string Name => "init"; 24 | 25 | public string HelpMessage => @" 26 | Inits current directory as 'cement tracked' 27 | 28 | Usage: 29 | cm init 30 | 31 | Note: 32 | $HOME directory cannot be used with this command 33 | "; 34 | 35 | public int Run(string[] args) 36 | { 37 | if (args.Length != 1) 38 | { 39 | consoleWriter.WriteError("Invalid command usage. User 'cm help init' for details"); 40 | return -1; 41 | } 42 | 43 | var cwd = Directory.GetCurrentDirectory(); 44 | var home = Helper.HomeDirectory(); 45 | 46 | if (cwd == home) 47 | { 48 | consoleWriter.WriteError("$HOME cannot be used as cement base directory"); 49 | return -1; 50 | } 51 | 52 | if (Helper.IsCementTrackedDirectory(cwd)) 53 | { 54 | consoleWriter.WriteInfo("It is already cement tracked directory"); 55 | return 0; 56 | } 57 | 58 | Directory.CreateDirectory(Helper.CementDirectory); 59 | consoleWriter.WriteOk(Directory.GetCurrentDirectory() + " became cement tracked directory."); 60 | return 0; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/YamlParsers/SettingsYamlParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using Cement.Cli.Common.Exceptions; 5 | 6 | namespace Cement.Cli.Common.YamlParsers; 7 | 8 | public sealed class SettingsYamlParser : ConfigurationYamlParser 9 | { 10 | private const string SectionName = "settings"; 11 | 12 | public SettingsYamlParser(FileInfo moduleName) 13 | : base(moduleName) 14 | { 15 | } 16 | 17 | public SettingsYamlParser(string moduleName, string text) 18 | : base(moduleName, text) 19 | { 20 | } 21 | 22 | public ModuleSettings Get() 23 | { 24 | try 25 | { 26 | var configDict = GetConfigurationSection("default"); 27 | return GetSettingsFromSection(configDict); 28 | } 29 | catch (BadYamlException) 30 | { 31 | throw; 32 | } 33 | catch (Exception exception) 34 | { 35 | throw new BadYamlException(ModuleName, SectionName, exception.Message); 36 | } 37 | } 38 | 39 | private static ModuleSettings GetSettingsFromSection(Dictionary configDict) 40 | { 41 | if (configDict == null || !configDict.ContainsKey(SectionName)) 42 | return new ModuleSettings(); 43 | if (configDict[SectionName] is not Dictionary settingsDict) 44 | return new ModuleSettings(); 45 | 46 | return GetSettings(settingsDict); 47 | } 48 | 49 | private static ModuleSettings GetSettings(Dictionary settingsDict) 50 | { 51 | return new ModuleSettings 52 | { 53 | IsContentModule = settingsDict.TryGetValue("type", out var type) && ((string)type).Trim() == "content", 54 | }; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/LsCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Common.Exceptions; 3 | using NDesk.Options; 4 | 5 | namespace Cement.Cli.Commands.OptionsParsers; 6 | 7 | public sealed class LsCommandOptionsParser : OptionsParser 8 | { 9 | public override LsCommandOptions Parse(string[] args) 10 | { 11 | var isLocal = false; 12 | var isAllModules = false; 13 | string branchName = null; 14 | var isSimpleMode = false; 15 | var showUrl = false; 16 | var showPushUrl = false; 17 | 18 | var parser = new OptionSet 19 | { 20 | {"l|local", _ => isLocal = true}, 21 | {"a|all", _ => isAllModules = true}, 22 | {"b|has-branch=", branch => branchName = branch}, 23 | {"u|url", _ => showUrl = true}, 24 | {"p|pushurl", _ => showPushUrl = true}, 25 | {"simple", _ => isSimpleMode = true} 26 | }; 27 | 28 | var extraArgs = parser.Parse(args.Skip(1)); 29 | ThrowIfHasExtraArgs(extraArgs); 30 | 31 | var moduleProcessType = GetModuleProcessType(isLocal, isAllModules, branchName); 32 | return new LsCommandOptions(isSimpleMode, moduleProcessType, showUrl, showPushUrl, branchName); 33 | } 34 | 35 | private static ModuleProcessType GetModuleProcessType(bool isLocal, bool isAllModules, string branchName) 36 | { 37 | if (isLocal && isAllModules) 38 | throw new BadArgumentException("Bad arguments: all and local"); 39 | 40 | if (isLocal) 41 | return ModuleProcessType.Local; 42 | 43 | if (isAllModules) 44 | return ModuleProcessType.All; 45 | 46 | return branchName is null 47 | ? ModuleProcessType.All 48 | : ModuleProcessType.Local; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/DepsValidatorsTests/DepsValidatorTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Cement.Cli.Common; 3 | using Cement.Cli.Common.DepsValidators; 4 | using FluentAssertions; 5 | using NUnit.Framework; 6 | 7 | namespace Cement.Cli.Tests.DepsValidatorsTests; 8 | 9 | [TestFixture] 10 | public class DepsValidatorTests 11 | { 12 | private readonly DepsValidator validator; 13 | 14 | public DepsValidatorTests() 15 | { 16 | validator = new DepsValidator("full-build"); 17 | } 18 | 19 | [Test] 20 | public void Should_validate_result_be_valid_when_configuration_has_no_deps() 21 | { 22 | // arrange 23 | var deps = Array.Empty(); 24 | 25 | // act 26 | var validateResult = validator.Validate(deps, out var validateErrors); 27 | 28 | //assert 29 | validateResult.Should().Be(DepsValidateResult.Valid); 30 | validateErrors.Should().BeEmpty(); 31 | } 32 | 33 | [Test] 34 | public void Should_validate_result_be_valid_when_all_deps_are_unique() 35 | { 36 | // arrange 37 | var uniqueDeps = TestDepsFactory.GetUniqueDeps(); 38 | 39 | // act 40 | var validateResult = validator.Validate(uniqueDeps, out var validateErrors); 41 | 42 | //assert 43 | validateResult.Should().Be(DepsValidateResult.Valid); 44 | validateErrors.Should().BeEmpty(); 45 | } 46 | 47 | [Test] 48 | public void Should_validate_result_be_invalid_when_deps_are_non_unique() 49 | { 50 | // arrange 51 | var uniqueDeps = TestDepsFactory.GetNonUniqueDeps(); 52 | 53 | // act 54 | var validateResult = validator.Validate(uniqueDeps, out var validateErrors); 55 | 56 | //assert 57 | validateResult.Should().Be(DepsValidateResult.Invalid); 58 | validateErrors.Should().NotBeEmpty(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ModuleCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Cement.Cli.Common; 4 | using JetBrains.Annotations; 5 | 6 | namespace Cement.Cli.Commands; 7 | 8 | [PublicAPI] 9 | public sealed class ModuleCommand : ICommand 10 | { 11 | private readonly ConsoleWriter consoleWriter; 12 | private readonly ICommandActivator commandActivator; 13 | private readonly Dictionary commands; 14 | 15 | public ModuleCommand(ConsoleWriter consoleWriter, ICommandActivator commandActivator) 16 | { 17 | this.consoleWriter = consoleWriter; 18 | this.commandActivator = commandActivator; 19 | commands = new Dictionary 20 | { 21 | {"add", typeof(AddModuleCommand)}, 22 | {"change", typeof(ChangeModuleCommand)} 23 | }; 24 | } 25 | 26 | public bool MeasureElapsedTime { get; } 27 | 28 | public bool RequireModuleYaml { get; } 29 | 30 | public CommandLocation Location { get; } = CommandLocation.Any; 31 | 32 | public string Name => "module"; 33 | 34 | public string HelpMessage => @" 35 | Adds new or changes existing cement module 36 | Don't delete old modules 37 | 38 | Usage: 39 | cm module module_name module_fetch_url [-p|--pushurl=module_push_url] [--package=package_name] 40 | --pushurl - module push url 41 | --package - name of repository with modules description, specify if multiple 42 | "; 43 | 44 | public int Run(string[] args) 45 | { 46 | if (args.Length < 2 || !commands.ContainsKey(args[1])) 47 | { 48 | consoleWriter.WriteError("Bad arguments"); 49 | return -1; 50 | } 51 | 52 | var commandType = commands[args[1]]; 53 | var command = (ICommand)commandActivator.Create(commandType); 54 | 55 | return command.Run(args); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/Helpers/YamlFromText.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Cement.Cli.Common; 3 | using Cement.Cli.Common.DepsValidators; 4 | using Cement.Cli.Common.YamlParsers; 5 | 6 | namespace Cement.Cli.Tests.Helpers; 7 | 8 | public static class YamlFromText 9 | { 10 | public static ConfigurationYamlParser ConfigurationParser(string text) 11 | { 12 | const string fakeModuleName = "some_module"; 13 | return new ConfigurationYamlParser(fakeModuleName, text); 14 | } 15 | 16 | public static DepsYamlParser DepsParser(string text) 17 | { 18 | using var dir = new TempDirectory(); 19 | var yamlPath = Path.Combine(dir.Path, Helper.YamlSpecFile); 20 | File.WriteAllText(yamlPath, text); 21 | return new DepsYamlParser(ConsoleWriter.Shared, DepsValidatorFactory.Shared, new FileInfo(dir.Path)); 22 | } 23 | 24 | public static InstallYamlParser InstallParser(string text) 25 | { 26 | const string fakeModuleName = "some_module"; 27 | return new InstallYamlParser(fakeModuleName, text); 28 | } 29 | 30 | public static BuildYamlParser BuildParser(string text) 31 | { 32 | using var dir = new TempDirectory(); 33 | var yamlPath = Path.Combine(dir.Path, Helper.YamlSpecFile); 34 | File.WriteAllText(yamlPath, text); 35 | return new BuildYamlParser(new FileInfo(dir.Path)); 36 | } 37 | 38 | public static SettingsYamlParser SettingsParser(string text) 39 | { 40 | using var dir = new TempDirectory(); 41 | var yamlPath = Path.Combine(dir.Path, Helper.YamlSpecFile); 42 | File.WriteAllText(yamlPath, text); 43 | return new SettingsYamlParser(new FileInfo(dir.Path)); 44 | } 45 | 46 | public static HooksYamlParser HooksParser(string text) 47 | { 48 | const string fakeModuleName = "some_module"; 49 | return new HooksYamlParser(fakeModuleName, text); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/IniParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace Cement.Cli.Common; 6 | 7 | public sealed class IniParser 8 | { 9 | private readonly Dictionary> ini; 10 | 11 | public IniParser() 12 | { 13 | ini = new Dictionary>(StringComparer.InvariantCultureIgnoreCase); 14 | } 15 | 16 | public IniData ParseString(string txt) 17 | { 18 | ini.Clear(); 19 | var currentSection = new Dictionary(StringComparer.InvariantCultureIgnoreCase); 20 | 21 | ini[""] = currentSection; 22 | var currentOption = ""; 23 | 24 | foreach (var line in txt.Split(new[] {"\n"}, StringSplitOptions.RemoveEmptyEntries) 25 | .Where(t => !string.IsNullOrWhiteSpace(t)) 26 | .Select(t => t.Trim())) 27 | { 28 | if (line.StartsWith(";") || line.StartsWith("#")) 29 | continue; 30 | 31 | if (line.StartsWith("[") && line.EndsWith("]")) 32 | { 33 | currentOption = ""; 34 | currentSection = new Dictionary(StringComparer.InvariantCultureIgnoreCase); 35 | ini[line.Substring(1, line.LastIndexOf("]") - 1)] = currentSection; 36 | continue; 37 | } 38 | 39 | var idx = line.IndexOf("="); 40 | if (idx == -1) 41 | { 42 | if (currentOption != "") 43 | currentSection[currentOption] += "\r\n" + line; 44 | } 45 | else 46 | { 47 | currentSection[line.Substring(0, idx).Trim()] = line.Substring(idx + 1).Trim(); 48 | currentOption = line.Substring(0, idx).Trim(); 49 | } 50 | } 51 | 52 | return new IniData(ini); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/ParsersTests/V2/TestHooksSectionParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Cement.Cli.Tests.Helpers; 3 | using Cement.Cli.Common.YamlParsers.V2; 4 | using FluentAssertions; 5 | using NUnit.Framework; 6 | using SharpYaml.Serialization; 7 | 8 | namespace Cement.Cli.Tests.ParsersTests.V2; 9 | 10 | [TestFixture] 11 | public class TestHooksSectionParser 12 | { 13 | private static TestCaseData[] Source = 14 | { 15 | new TestCaseData( 16 | @" 17 | default: 18 | hooks: 19 | ", 20 | new string[0]) 21 | .SetName("Empty hooks section"), 22 | 23 | new TestCaseData( 24 | @" 25 | default: 26 | hooks: 27 | - a 28 | - b/c 29 | ", 30 | new[] {"a", "b/c"}) 31 | .SetName("Two hooks in defaults section") 32 | }; 33 | 34 | [TestCaseSource(nameof(Source))] 35 | public void TestParse(string input, string[] expectedResult) 36 | { 37 | var parser = new HooksSectionParser(); 38 | var hooks = GetHooksSections(input); 39 | 40 | var actual = parser.Parse(hooks); 41 | 42 | actual.Should().BeEquivalentTo(expectedResult, o => o.WithStrictOrdering()); 43 | } 44 | 45 | [TestCaseSource(nameof(Source))] 46 | public void TestParse2(string input, string[] expectedResult) 47 | { 48 | var actual = YamlFromText.HooksParser(input).Get(); 49 | 50 | actual.Should().BeEquivalentTo(expectedResult, o => o.WithStrictOrdering()); 51 | } 52 | 53 | private object GetHooksSections(string text) 54 | { 55 | var serializer = new Serializer(); 56 | var yaml = (Dictionary)serializer.Deserialize(text); 57 | 58 | var defaultSection = yaml["default"] as Dictionary; 59 | 60 | object hooks = null; 61 | defaultSection?.TryGetValue("hooks", out hooks); 62 | return hooks; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/PackCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using Cement.Cli.Common; 2 | using Cement.Cli.Common.Exceptions; 3 | using NDesk.Options; 4 | 5 | namespace Cement.Cli.Commands.OptionsParsers; 6 | 7 | public sealed class PackCommandOptionsParser : OptionsParser 8 | { 9 | public override PackCommandOptions Parse(string[] args) 10 | { 11 | string configuration = null; 12 | var warnings = false; 13 | var obsolete = false; 14 | var verbose = false; 15 | var progress = false; 16 | var preRelease = false; 17 | 18 | var parser = new OptionSet 19 | { 20 | {"c|configuration=", conf => configuration = conf}, 21 | {"w|warnings", _ => warnings = true}, 22 | {"W", _ => obsolete = true}, 23 | {"v|verbose", _ => verbose = true}, 24 | {"p|progress", _ => progress = true}, 25 | {"prerelease", _ => preRelease = true} 26 | }; 27 | 28 | args = parser.Parse(args).ToArray(); 29 | 30 | if (args.Length != 2 || args[0] != "pack") 31 | throw new BadArgumentException("Wrong usage of command.\nUsage: cm pack [-c|--configuration ] "); 32 | 33 | var project = args[1]; 34 | 35 | // todo(dstarasov): кажется, это не ответственность парсера, а скорее какого-то валидатора или самой команды 36 | if (!project.EndsWith(".csproj")) 37 | throw new BadArgumentException(project + " is not csproj file"); 38 | 39 | var buildSettings = new BuildSettings 40 | { 41 | ShowAllWarnings = warnings, 42 | ShowObsoleteWarnings = obsolete, 43 | ShowOutput = verbose, 44 | ShowProgress = progress, 45 | ShowWarningsSummary = true 46 | }; 47 | 48 | return new PackCommandOptions(project, configuration, buildSettings, preRelease); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/OptionsParsers/UsagesShowCommandOptionsParser.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using Cement.Cli.Common; 4 | using Cement.Cli.Common.Exceptions; 5 | using NDesk.Options; 6 | 7 | namespace Cement.Cli.Commands.OptionsParsers; 8 | 9 | public sealed class UsagesShowCommandOptionsParser : OptionsParser 10 | { 11 | public override UsagesShowCommandOptions Parse(string[] args) 12 | { 13 | //todo(dstarasov): этот код выглядит скорее как ответственность уже самой команды, а не парсера 14 | var currentDir = Directory.GetCurrentDirectory(); 15 | while (currentDir != Directory.GetDirectoryRoot(currentDir) && !Helper.IsCurrentDirectoryModule(currentDir)) 16 | currentDir = Directory.GetParent(currentDir).FullName; 17 | 18 | var configuration = "*"; 19 | var branch = "*"; 20 | var all = false; 21 | var edges = false; 22 | 23 | string module = null; 24 | if (Helper.IsCurrentDirectoryModule(currentDir)) 25 | module = Path.GetFileName(currentDir); 26 | 27 | var parser = new OptionSet 28 | { 29 | {"c|configuration=", conf => configuration = conf}, 30 | {"m|module=", m => module = m}, 31 | {"b|branch=", b => branch = b}, 32 | {"a|all", _ => all = true}, 33 | {"e|edges", _ => edges = true} 34 | }; 35 | var extraArgs = parser.Parse(args.Skip(2)); 36 | if (module == null) 37 | { 38 | throw new BadArgumentException("Current directory is not cement module directory, use -m to specify module name"); 39 | } 40 | 41 | if (module.Contains('/')) 42 | { 43 | var parts = module.Split('/'); 44 | 45 | module = parts[0]; 46 | configuration = parts[1]; 47 | } 48 | 49 | ThrowIfHasExtraArgs(extraArgs); 50 | return new UsagesShowCommandOptions(module, branch, configuration, all, edges); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/UserCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Linq; 3 | using Cement.Cli.Commands.Attributes; 4 | using Cement.Cli.Common; 5 | using JetBrains.Annotations; 6 | using Microsoft.Extensions.Logging; 7 | 8 | namespace Cement.Cli.Commands; 9 | 10 | [PublicAPI] 11 | [HiddenCommand] 12 | public sealed class UserCommand : Command 13 | { 14 | private readonly ILogger logger; 15 | private readonly ConsoleWriter consoleWriter; 16 | 17 | public UserCommand(ILogger logger, ConsoleWriter consoleWriter) 18 | { 19 | this.logger = logger; 20 | this.consoleWriter = consoleWriter; 21 | } 22 | 23 | public override string Name => ""; 24 | public override string HelpMessage => @""; 25 | 26 | protected override int Execute(UserCommandOptions options) 27 | { 28 | var arguments = options.Arguments; 29 | var cmd = CementSettingsRepository.Get().UserCommands[arguments[0]]; 30 | logger.LogDebug("Run command {CommandName}: '{Cmd}'", arguments[0], cmd); 31 | if (arguments.Length > 1) 32 | { 33 | arguments = arguments.Skip(1).ToArray(); 34 | cmd = string.Format(cmd, arguments); 35 | } 36 | 37 | return Run(cmd); 38 | } 39 | 40 | protected override UserCommandOptions ParseArgs(string[] args) 41 | { 42 | return new UserCommandOptions(args); 43 | } 44 | 45 | private int Run(string cmd) 46 | { 47 | consoleWriter.WriteInfo($"Running command '{cmd}'"); 48 | 49 | var startInfo = new ProcessStartInfo 50 | { 51 | FileName = Platform.IsUnix() ? "/bin/bash" : "cmd", 52 | Arguments = Platform.IsUnix() ? " -lc " : " /c ", 53 | UseShellExecute = false 54 | }; 55 | startInfo.Arguments = startInfo.Arguments + "\"" + cmd + "\""; 56 | 57 | var process = Process.Start(startInfo); 58 | process.WaitForExit(); 59 | return process.ExitCode; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/CompleteCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Cement.Cli.Commands.Attributes; 4 | using Cement.Cli.Common; 5 | using JetBrains.Annotations; 6 | using Microsoft.Extensions.Logging; 7 | 8 | namespace Cement.Cli.Commands; 9 | 10 | [PublicAPI] 11 | [HiddenCommand] 12 | public sealed class CompleteCommand : Command 13 | { 14 | private readonly ILogger logger; 15 | private readonly ConsoleWriter consoleWriter; 16 | private readonly CompleteCommandAutomata completeCommandAutomata; 17 | 18 | public CompleteCommand(ILogger logger, ConsoleWriter consoleWriter, 19 | CompleteCommandAutomata completeCommandAutomata) 20 | { 21 | this.logger = logger; 22 | this.consoleWriter = consoleWriter; 23 | this.completeCommandAutomata = completeCommandAutomata; 24 | } 25 | 26 | public override string Name => "complete"; 27 | public override string HelpMessage => ""; 28 | 29 | protected override int Execute(CompleteCommandOptions options) 30 | { 31 | var otherArgs = options.OtherArgs; 32 | var buffer = otherArgs.Length == 0 33 | ? "" 34 | : otherArgs[0]; 35 | 36 | if (otherArgs.Length > 1) 37 | { 38 | int pos; 39 | if (int.TryParse(otherArgs[1], out pos) && buffer.Length > pos) 40 | buffer = buffer.Substring(0, pos); 41 | } 42 | 43 | logger.LogDebug("[COMPLETE] '{CompleteBuffer}'", buffer); 44 | var result = completeCommandAutomata.Complete(buffer); 45 | PrintList(result); 46 | 47 | return 0; 48 | } 49 | 50 | protected override CompleteCommandOptions ParseArgs(string[] args) 51 | { 52 | var otherArgs = args.Skip(1).ToArray(); 53 | return new CompleteCommandOptions(otherArgs); 54 | } 55 | 56 | private void PrintList(IEnumerable list) 57 | { 58 | consoleWriter.WriteLines(list.OrderBy(x => x)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Cement.Cli.Common/Console/WindowsConsole.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Cement.Cli.Common.Console; 5 | 6 | public sealed class WindowsConsole : IConsole 7 | { 8 | private readonly TextWriter writer; 9 | 10 | public WindowsConsole(TextWriter writer) 11 | { 12 | this.writer = writer; 13 | } 14 | 15 | public void Write(string message, ConsoleColor? backgroundColor = default, ConsoleColor? foregroundColor = default) 16 | { 17 | var colorChanged = SetColor(backgroundColor, foregroundColor); 18 | 19 | writer.Write(message); 20 | 21 | if (colorChanged) 22 | ResetColor(); 23 | } 24 | 25 | public void ClearLine() 26 | { 27 | writer.Write($"\r{{0,-{WindowWidth - 1}}}\r", ""); 28 | } 29 | 30 | public int WindowWidth => CalculateWindowWidth(); 31 | 32 | private static bool SetColor(ConsoleColor? background, ConsoleColor? foreground) 33 | { 34 | var backgroundChanged = SetBackgroundColor(background); 35 | return SetForegroundColor(foreground) || backgroundChanged; 36 | } 37 | 38 | private static void ResetColor() 39 | { 40 | System.Console.ResetColor(); 41 | } 42 | 43 | private static bool SetBackgroundColor(ConsoleColor? background) 44 | { 45 | if (!background.HasValue) 46 | return false; 47 | 48 | System.Console.BackgroundColor = background.Value; 49 | return true; 50 | } 51 | 52 | private static bool SetForegroundColor(ConsoleColor? foreground) 53 | { 54 | if (!foreground.HasValue) 55 | return false; 56 | 57 | System.Console.ForegroundColor = foreground.Value; 58 | return true; 59 | } 60 | 61 | private static int CalculateWindowWidth() 62 | { 63 | var result = 80; 64 | try 65 | { 66 | result = Math.Max(result, System.Console.WindowWidth); 67 | } 68 | catch 69 | { 70 | // ignored 71 | } 72 | 73 | return result; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/AddModuleCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Commands.OptionsParsers; 3 | using Cement.Cli.Common; 4 | using Cement.Cli.Common.Exceptions; 5 | using JetBrains.Annotations; 6 | 7 | namespace Cement.Cli.Commands; 8 | 9 | [PublicAPI] 10 | public sealed class AddModuleCommand : Command 11 | { 12 | private readonly ConsoleWriter consoleWriter; 13 | private readonly IPackageUpdater packageUpdater; 14 | private readonly ModuleHelper moduleHelper; 15 | 16 | public AddModuleCommand(ConsoleWriter consoleWriter, IPackageUpdater packageUpdater, ModuleHelper moduleHelper) 17 | { 18 | this.consoleWriter = consoleWriter; 19 | this.packageUpdater = packageUpdater; 20 | this.moduleHelper = moduleHelper; 21 | } 22 | 23 | public override string Name => "add"; 24 | public override string HelpMessage => @""; 25 | 26 | protected override int Execute(AddModuleCommandOptions options) 27 | { 28 | packageUpdater.UpdatePackages(); 29 | var packages = Helper.GetPackages(); 30 | 31 | var packageName = options.PackageName; 32 | if (packages.Count > 1 && packageName == null) 33 | throw new CementException($"Specify --package={string.Join("|", packages.Select(p => p.Name))}"); 34 | 35 | var package = packageName == null 36 | ? packages.FirstOrDefault(p => p.Type == "git") 37 | : packages.FirstOrDefault(p => p.Name == packageName); 38 | 39 | if (package == null) 40 | throw new CementException("Unable to find " + packageName + " in package list"); 41 | 42 | if (package.Type == "git") 43 | return moduleHelper.AddModule(package, options.ModuleName, options.PushUrl, options.FetchUrl); 44 | 45 | consoleWriter.WriteError("You should add local modules file manually"); 46 | return -1; 47 | } 48 | 49 | protected override AddModuleCommandOptions ParseArgs(string[] args) 50 | { 51 | return new AddModuleCommandOptionsParser().Parse(args); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/ChangeModuleCommand.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Cement.Cli.Commands.OptionsParsers; 3 | using Cement.Cli.Common; 4 | using Cement.Cli.Common.Exceptions; 5 | using JetBrains.Annotations; 6 | 7 | namespace Cement.Cli.Commands; 8 | 9 | [PublicAPI] 10 | public sealed class ChangeModuleCommand : Command 11 | { 12 | private readonly ConsoleWriter consoleWriter; 13 | private readonly IPackageUpdater packageUpdater; 14 | private readonly ModuleHelper moduleHelper; 15 | 16 | public ChangeModuleCommand(ConsoleWriter consoleWriter, IPackageUpdater packageUpdater, ModuleHelper moduleHelper) 17 | { 18 | this.consoleWriter = consoleWriter; 19 | this.packageUpdater = packageUpdater; 20 | this.moduleHelper = moduleHelper; 21 | } 22 | 23 | public override string Name => "add"; 24 | public override string HelpMessage => @""; 25 | 26 | protected override int Execute(ChangeModuleCommandOptions options) 27 | { 28 | packageUpdater.UpdatePackages(); 29 | var packages = Helper.GetPackages(); 30 | 31 | var packageName = options.PackageName; 32 | if (packages.Count > 1 && packageName == null) 33 | throw new CementException($"Specify --package={string.Join("|", packages.Select(p => p.Name))}"); 34 | 35 | var package = packageName == null 36 | ? packages.FirstOrDefault(p => p.Type == "git") 37 | : packages.FirstOrDefault(p => p.Name == packageName); 38 | 39 | if (package == null) 40 | throw new CementException("Unable to find " + packageName + " in package list"); 41 | 42 | if (package.Type == "git") 43 | return moduleHelper.ChangeModule(package, options.ModuleName, options.PushUrl, options.FetchUrl); 44 | 45 | consoleWriter.WriteError("You should add local modules file manually"); 46 | return -1; 47 | } 48 | 49 | protected override ChangeModuleCommandOptions ParseArgs(string[] args) 50 | { 51 | return new ChangeModuleCommandOptionsParser().Parse(args); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Cement.Cli.Commands/PackagesCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Cement.Cli.Common; 5 | using JetBrains.Annotations; 6 | 7 | namespace Cement.Cli.Commands; 8 | 9 | [PublicAPI] 10 | public sealed class PackagesCommand : ICommand 11 | { 12 | private readonly ConsoleWriter consoleWriter; 13 | private readonly ICommandActivator commandActivator; 14 | private readonly IDictionary subcommands; 15 | 16 | public PackagesCommand(ConsoleWriter consoleWriter, ICommandActivator commandActivator) 17 | { 18 | this.consoleWriter = consoleWriter; 19 | this.commandActivator = commandActivator; 20 | subcommands = new Dictionary 21 | { 22 | {"list", typeof(ListPackagesCommand)}, 23 | {"add", typeof(AddPackageCommand)}, 24 | {"remove", typeof(RemovePackageCommand)} 25 | }; 26 | } 27 | 28 | public bool MeasureElapsedTime { get; } 29 | 30 | public bool RequireModuleYaml { get; } 31 | 32 | public CommandLocation Location { get; } = CommandLocation.Any; 33 | 34 | public string Name => "packages"; 35 | 36 | public string HelpMessage => @" 37 | Manage set of packages 38 | 39 | usage: cm packages list 40 | or: cm packages add 41 | or: cm packages remove 42 | "; 43 | 44 | public int Run(string[] args) 45 | { 46 | if (args.Length >= 2) 47 | { 48 | if (subcommands.ContainsKey(args[1])) 49 | { 50 | var commandType = subcommands[args[1]]; 51 | var command = (ICommand)commandActivator.Create(commandType); 52 | 53 | return command.Run(args.Skip(2).ToArray()); 54 | } 55 | 56 | consoleWriter.WriteError($"Unknown subcommand: {args[1]}{Environment.NewLine}{HelpMessage}"); 57 | } 58 | else 59 | { 60 | consoleWriter.WriteError($"Unknown subcommand{Environment.NewLine}{HelpMessage}"); 61 | } 62 | 63 | return -1; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/ParsersTests/V2/TestSettingsSectionParser.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Cement.Cli.Common.YamlParsers; 3 | using Cement.Cli.Common.YamlParsers.V2; 4 | using FluentAssertions; 5 | using NUnit.Framework; 6 | using SharpYaml.Serialization; 7 | 8 | namespace Cement.Cli.Tests.ParsersTests.V2; 9 | 10 | [TestFixture] 11 | public class TestSettingsSectionParser 12 | { 13 | private static TestCaseData[] Source = 14 | { 15 | new TestCaseData( 16 | @" 17 | default: 18 | ", 19 | new ModuleSettings()) 20 | .SetName("No settings section"), 21 | 22 | new TestCaseData( 23 | @" 24 | default: 25 | settings: 26 | ", 27 | new ModuleSettings()) 28 | .SetName("Empty settings section"), 29 | 30 | new TestCaseData( 31 | @" 32 | default: 33 | settings: 34 | somekey: somevalue 35 | ", 36 | new ModuleSettings()) 37 | .SetName("Settings section with unknown key-value"), 38 | 39 | new TestCaseData( 40 | @" 41 | default: 42 | settings: 43 | type: content 44 | ", 45 | new ModuleSettings {IsContentModule = true}) 46 | .SetName("Settings section with 'type: content'") 47 | }; 48 | 49 | [TestCaseSource(nameof(Source))] 50 | public void TestParse(string input, ModuleSettings expectedResult) 51 | { 52 | var parser = new SettingsSectionParser(); 53 | var settings = GetSettingsSection(input); 54 | 55 | var actual = parser.Parse(settings); 56 | 57 | actual.Should().BeEquivalentTo(expectedResult, o => o.WithStrictOrdering()); 58 | } 59 | 60 | private object GetSettingsSection(string text) 61 | { 62 | var serializer = new Serializer(); 63 | var yaml = (Dictionary)serializer.Deserialize(text); 64 | 65 | var defaultSection = yaml["default"] as Dictionary; 66 | 67 | object hooks = null; 68 | defaultSection?.TryGetValue("settings", out hooks); 69 | return hooks; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/Cement.Cli.Tests/OptionsParsers/ShowDepsCommandOptionsParserTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Cement.Cli.Commands; 3 | using Cement.Cli.Commands.OptionsParsers; 4 | using Cement.Cli.Common.Exceptions; 5 | using FluentAssertions; 6 | using NUnit.Framework; 7 | 8 | namespace Cement.Cli.Tests.OptionsParsers; 9 | 10 | [TestFixture] 11 | public sealed class ShowDepsCommandOptionsParserTests 12 | { 13 | private readonly ShowDepsCommandOptionsParser parser; 14 | 15 | public ShowDepsCommandOptionsParserTests() 16 | { 17 | parser = new ShowDepsCommandOptionsParser(); 18 | } 19 | 20 | public static IEnumerable TestCases 21 | { 22 | get 23 | { 24 | var args1 = new[] {"show-deps"}; 25 | var expected1 = new ShowDepsCommandOptions(null); 26 | yield return new TestCaseData(args1, expected1) {TestName = ""}; 27 | 28 | const string configuration = "configuration"; 29 | 30 | var args2 = new[] {"show-deps", "-c", configuration}; 31 | var expected2 = new ShowDepsCommandOptions(configuration); 32 | yield return new TestCaseData(args2, expected2) {TestName = "-c "}; 33 | } 34 | } 35 | 36 | public static IEnumerable FaultTestCases 37 | { 38 | get 39 | { 40 | var args1 = (object)new[] {"show-deps", "--extra_argument1"}; 41 | yield return new TestCaseData(args1) {TestName = "extra_arguments"}; 42 | } 43 | } 44 | 45 | [TestCaseSource(nameof(TestCases))] 46 | public void Should_parse(string[] args, ShowDepsCommandOptions expected) 47 | { 48 | // arrange 49 | // act 50 | var actual = parser.Parse(args); 51 | 52 | // assert 53 | actual.Should().BeEquivalentTo(expected); 54 | } 55 | 56 | [TestCaseSource(nameof(FaultTestCases))] 57 | public void Should_fail(string[] args) 58 | { 59 | // arrange 60 | // act 61 | var act = () => parser.Parse(args); 62 | 63 | // assert 64 | act.Should().ThrowExactly(); 65 | } 66 | } 67 | --------------------------------------------------------------------------------