55using CliFx . Infrastructure ;
66using Husky . Services . Contracts ;
77using Husky . Stdout ;
8+ using Husky . Utils ;
89
910namespace Husky . Cli ;
1011
@@ -16,6 +17,8 @@ public class InstallCommand : CommandBase
1617 private readonly IFileSystem _fileSystem ;
1718 private const string FailedMsg = "Git hooks installation failed" ;
1819 private const string HUSKY_FOLDER_NAME = ".husky" ;
20+ private const string GIT_ATTRIBUTES_FILE_NAME = ".gitattributes" ;
21+ private const string GIT_ATTRIBUTES_CONTENT = "* text eol=lf\n " ;
1922 private const string DOCS_URL = "https://alirezanet.github.io/Husky.Net/guide/getting-started" ;
2023
2124 [ CommandOption ( "dir" , 'd' , Description = "The custom directory to install Husky hooks." ) ]
@@ -115,39 +118,6 @@ private void RunUnderMutexControl(string path, string cwd)
115118 }
116119 }
117120
118- private void CreateResources ( string path )
119- {
120- $ "Creating resources and configuration files in '{ path } '". LogVerbose ( ) ;
121-
122- // Create .husky/_
123- _fileSystem . Directory . CreateDirectory ( Path . Combine ( path , "_" ) ) ;
124-
125- // Create .husky/_/. ignore
126- _fileSystem . File . WriteAllText ( Path . Combine ( path , "_/.gitignore" ) , "*" ) ;
127-
128- // Copy husky.sh to .husky/_/husky.sh
129- var husky_shPath = Path . Combine ( path , "_" , "husky.sh" ) ;
130- {
131- using var stream = Assembly . GetAssembly ( typeof ( Program ) ) ! . GetManifestResourceStream ( "Husky.templates.husky.sh" ) ! ;
132- using var sr = new StreamReader ( stream ) ;
133- var content = sr . ReadToEnd ( ) ;
134- _fileSystem . File . WriteAllText ( husky_shPath , content ) ;
135- }
136-
137- // here we have to run the `ConfigureGitAndFilePermission` synchronously because mutex will fail if thread changes
138- ConfigureGitAndFilePermission ( path , husky_shPath ) . GetAwaiter ( ) . GetResult ( ) ;
139-
140- // Created task-runner.json file
141- // We don't want to override this file
142- if ( ! _fileSystem . File . Exists ( Path . Combine ( path , "task-runner.json" ) ) )
143- {
144- using var stream = Assembly . GetAssembly ( typeof ( Program ) ) ! . GetManifestResourceStream ( "Husky.templates.task-runner.json" ) ! ;
145- using var sr = new StreamReader ( stream ) ;
146- var content = sr . ReadToEnd ( ) ;
147- _fileSystem . File . WriteAllText ( Path . Combine ( path , "task-runner.json" ) , content ) ;
148- }
149- }
150-
151121 private async Task CreateResourcesAsync ( string path )
152122 {
153123 $ "Creating resources and configuration files asynchronously in '{ path } '". LogVerbose ( ) ;
@@ -158,12 +128,17 @@ private async Task CreateResourcesAsync(string path)
158128 // Create .husky/_/. ignore
159129 await _fileSystem . File . WriteAllTextAsync ( Path . Combine ( path , "_/.gitignore" ) , "*" ) ;
160130
131+ // Keep Husky hook scripts LF-only even when users have core.autocrlf enabled.
132+ var gitAttributesPath = Path . Combine ( path , GIT_ATTRIBUTES_FILE_NAME ) ;
133+ if ( ! _fileSystem . File . Exists ( gitAttributesPath ) )
134+ await _fileSystem . File . WriteAllTextAsync ( gitAttributesPath , GIT_ATTRIBUTES_CONTENT ) ;
135+
161136 // Copy husky.sh to .husky/_/husky.sh
162137 var husky_shPath = Path . Combine ( path , "_" , "husky.sh" ) ;
163138 {
164139 await using var stream = Assembly . GetAssembly ( typeof ( Program ) ) ! . GetManifestResourceStream ( "Husky.templates.husky.sh" ) ! ;
165140 using var sr = new StreamReader ( stream ) ;
166- var content = await sr . ReadToEndAsync ( ) ;
141+ var content = ShellScriptLineEndings . Normalize ( await sr . ReadToEndAsync ( ) ) ;
167142 await _fileSystem . File . WriteAllTextAsync ( husky_shPath , content ) ;
168143 }
169144
0 commit comments