1515using Azure . Functions . Cli . Interfaces ;
1616using Colors . Net ;
1717using Fclp ;
18- using Microsoft . Azure . WebJobs . Script ;
1918using static Azure . Functions . Cli . Common . OutputTheme ;
2019
2120namespace Azure . Functions . Cli . Actions . AzureActions
@@ -29,6 +28,8 @@ internal class PublishFunctionApp : BaseFunctionAppAction
2928 public bool PublishLocalSettings { get ; set ; }
3029 public bool OverwriteSettings { get ; set ; }
3130 public bool PublishLocalSettingsOnly { get ; set ; }
31+ public bool ListIgnoredFiles { get ; set ; }
32+ public bool ListIncludedFiles { get ; set ; }
3233
3334 public PublishFunctionApp ( IArmManager armManager , ISettings settings , ISecretsManager secretsManager )
3435 : base ( armManager )
@@ -51,62 +52,143 @@ public override ICommandLineParserResult ParseArgs(string[] args)
5152 . Setup < bool > ( 'y' , "overwrite-settings" )
5253 . WithDescription ( "Only to be used in conjunction with -i or -o. Overwrites AppSettings in Azure with local value if different. Default is prompt." )
5354 . Callback ( f => OverwriteSettings = f ) ;
55+ Parser
56+ . Setup < bool > ( "list-ignored-files" )
57+ . WithDescription ( "Displays a list of files that will be ignored from publishing based on .funcignore" )
58+ . Callback ( f => ListIgnoredFiles = f ) ;
59+ Parser
60+ . Setup < bool > ( "list-included-files" )
61+ . WithDescription ( "Displays a list of files that will be included in publishing based on .funcignore" )
62+ . Callback ( f => ListIncludedFiles = f ) ;
5463
5564 return base . ParseArgs ( args ) ;
5665 }
5766
5867 public override async Task RunAsync ( )
5968 {
60- ColoredConsole . WriteLine ( "Getting site publishing info..." ) ;
61- var functionApp = await _armManager . GetFunctionAppAsync ( FunctionAppName ) ;
62- if ( PublishLocalSettingsOnly )
69+ GitIgnoreParser ignoreParser = null ;
70+ try
6371 {
64- var isSuccessful = await PublishAppSettings ( functionApp ) ;
65- if ( ! isSuccessful )
72+ var path = Path . Combine ( Environment . CurrentDirectory , Constants . FuncIgnoreFile ) ;
73+ if ( FileSystemHelpers . FileExists ( path ) )
6674 {
67- return ;
75+ ignoreParser = new GitIgnoreParser ( FileSystemHelpers . ReadAllTextFromFile ( path ) ) ;
6876 }
6977 }
78+ catch { }
79+
80+ if ( ListIncludedFiles )
81+ {
82+ InternalListIncludedFiles ( ignoreParser ) ;
83+ }
84+ else if ( ListIgnoredFiles )
85+ {
86+ InternalListIgnoredFiles ( ignoreParser ) ;
87+ }
7088 else
7189 {
72- var functionAppRoot = ScriptHostHelpers . GetFunctionAppRootDirectory ( Environment . CurrentDirectory ) ;
73- ColoredConsole . WriteLine ( WarningColor ( $ "Publish { functionAppRoot } contents to an Azure Function App. Locally deleted files are not removed from destination.") ) ;
74- await RetryHelper . Retry ( async ( ) =>
90+ if ( PublishLocalSettingsOnly )
7591 {
76- using ( var client = await GetRemoteZipClient ( new Uri ( $ "https://{ functionApp . ScmUri } ") ) )
77- using ( var request = new HttpRequestMessage ( HttpMethod . Put , new Uri ( "api/zip/site/wwwroot" , UriKind . Relative ) ) )
78- {
79- request . Headers . IfMatch . Add ( EntityTagHeaderValue . Any ) ;
92+ await InternalPublishLocalSettingsOnly ( ) ;
93+ }
94+ else
95+ {
96+ await InternalPublishFunctionApp ( ignoreParser ) ;
97+ }
98+ }
99+ }
100+
101+ private async Task InternalPublishFunctionApp ( GitIgnoreParser ignoreParser )
102+ {
103+ ColoredConsole . WriteLine ( "Getting site publishing info..." ) ;
104+ var functionApp = await _armManager . GetFunctionAppAsync ( FunctionAppName ) ;
105+ var functionAppRoot = ScriptHostHelpers . GetFunctionAppRootDirectory ( Environment . CurrentDirectory ) ;
106+ ColoredConsole . WriteLine ( WarningColor ( $ "Publish { functionAppRoot } contents to an Azure Function App. Locally deleted files are not removed from destination.") ) ;
107+ await RetryHelper . Retry ( async ( ) =>
108+ {
109+ using ( var client = await GetRemoteZipClient ( new Uri ( $ "https://{ functionApp . ScmUri } ") ) )
110+ using ( var request = new HttpRequestMessage ( HttpMethod . Put , new Uri ( "api/zip/site/wwwroot" , UriKind . Relative ) ) )
111+ {
112+ request . Headers . IfMatch . Add ( EntityTagHeaderValue . Any ) ;
80113
81- ColoredConsole . WriteLine ( "Creating archive for current directory..." ) ;
114+ ColoredConsole . WriteLine ( "Creating archive for current directory..." ) ;
82115
83- request . Content = CreateZip ( functionAppRoot ) ;
116+ request . Content = CreateZip ( functionAppRoot , ignoreParser ) ;
84117
85- ColoredConsole . WriteLine ( "Uploading archive..." ) ;
86- var response = await client . SendAsync ( request ) ;
87- if ( ! response . IsSuccessStatusCode )
88- {
89- throw new CliException ( $ "Error uploading archive ({ response . StatusCode } ).") ;
90- }
118+ ColoredConsole . WriteLine ( "Uploading archive..." ) ;
119+ var response = await client . SendAsync ( request ) ;
120+ if ( ! response . IsSuccessStatusCode )
121+ {
122+ throw new CliException ( $ "Error uploading archive ({ response . StatusCode } ).") ;
123+ }
91124
92- response = await client . PostAsync ( "api/functions/synctriggers" , content : null ) ;
93- if ( ! response . IsSuccessStatusCode )
94- {
95- throw new CliException ( $ "Error calling sync triggers ({ response . StatusCode } ).") ;
96- }
125+ response = await client . PostAsync ( "api/functions/synctriggers" , content : null ) ;
126+ if ( ! response . IsSuccessStatusCode )
127+ {
128+ throw new CliException ( $ "Error calling sync triggers ({ response . StatusCode } ).") ;
129+ }
97130
98- if ( PublishLocalSettings )
131+ if ( PublishLocalSettings )
132+ {
133+ var isSuccessful = await PublishAppSettings ( functionApp ) ;
134+ if ( ! isSuccessful )
99135 {
100- var isSuccessful = await PublishAppSettings ( functionApp ) ;
101- if ( ! isSuccessful )
102- {
103- return ;
104- }
136+ return ;
105137 }
106-
107- ColoredConsole . WriteLine ( "Upload completed successfully." ) ;
108138 }
109- } , 2 ) ;
139+
140+ ColoredConsole . WriteLine ( "Upload completed successfully." ) ;
141+ }
142+ } , 2 ) ;
143+ }
144+
145+ private static IEnumerable < string > GetFiles ( string path )
146+ {
147+ return FileSystemHelpers . GetFiles ( path , new [ ] { ".git" , ".vscode" } , new [ ] { ".funcignore" , ".gitignore" , "appsettings.json" , "local.settings.json" , "project.lock.json" } ) ;
148+ }
149+
150+ private async Task InternalPublishLocalSettingsOnly ( )
151+ {
152+ ColoredConsole . WriteLine ( "Getting site publishing info..." ) ;
153+ var functionApp = await _armManager . GetFunctionAppAsync ( FunctionAppName ) ;
154+ var isSuccessful = await PublishAppSettings ( functionApp ) ;
155+ if ( ! isSuccessful )
156+ {
157+ return ;
158+ }
159+ }
160+
161+ private static void InternalListIgnoredFiles ( GitIgnoreParser ignoreParser )
162+ {
163+ if ( ignoreParser == null )
164+ {
165+ ColoredConsole . Error . WriteLine ( "No .funcignore file" ) ;
166+ return ;
167+ }
168+
169+ foreach ( var file in GetFiles ( Environment . CurrentDirectory ) . Select ( f => f . Replace ( Environment . CurrentDirectory , "" ) . Trim ( Path . DirectorySeparatorChar ) . Replace ( "\\ " , "/" ) ) )
170+ {
171+ if ( ignoreParser . Denies ( file ) )
172+ {
173+ ColoredConsole . WriteLine ( file ) ;
174+ }
175+ }
176+ }
177+
178+ private static void InternalListIncludedFiles ( GitIgnoreParser ignoreParser )
179+ {
180+ if ( ignoreParser == null )
181+ {
182+ ColoredConsole . Error . WriteLine ( "No .funcignore file" ) ;
183+ return ;
184+ }
185+
186+ foreach ( var file in GetFiles ( Environment . CurrentDirectory ) . Select ( f => f . Replace ( Environment . CurrentDirectory , "" ) . Trim ( Path . DirectorySeparatorChar ) . Replace ( "\\ " , "/" ) ) )
187+ {
188+ if ( ignoreParser . Accepts ( file ) )
189+ {
190+ ColoredConsole . WriteLine ( file ) ;
191+ }
110192 }
111193 }
112194
@@ -173,14 +255,17 @@ private IDictionary<string, string> MergeAppSettings(IDictionary<string, string>
173255 return result ;
174256 }
175257
176- private static StreamContent CreateZip ( string path )
258+ private static StreamContent CreateZip ( string path , GitIgnoreParser ignoreParser )
177259 {
178260 var memoryStream = new MemoryStream ( ) ;
179261 using ( var zip = new ZipArchive ( memoryStream , ZipArchiveMode . Create , leaveOpen : true ) )
180262 {
181- foreach ( var fileName in FileSystemHelpers . GetFiles ( path , new [ ] { ".git" , ".vscode" } , new [ ] { ".gitignore" , "appsettings.json" , "local.settings.json" , "project.lock.json" } ) )
263+ foreach ( var fileName in GetFiles ( path ) )
182264 {
183- zip . AddFile ( fileName , fileName , path ) ;
265+ if ( ignoreParser ? . Accepts ( fileName . Replace ( path , "" ) . Trim ( Path . DirectorySeparatorChar ) . Replace ( "\\ " , "/" ) ) ?? true )
266+ {
267+ zip . AddFile ( fileName , fileName , path ) ;
268+ }
184269 }
185270 }
186271 memoryStream . Seek ( 0 , SeekOrigin . Begin ) ;
0 commit comments