@@ -3,20 +3,20 @@ import * as clc from "colorette";
3
3
import { Options } from "../../../options" ;
4
4
import { logger } from "../../../logger" ;
5
5
import { reduceFlat } from "../../../functional" ;
6
+ import * as utils from "../../../utils" ;
6
7
import * as args from "../args" ;
7
8
import * as backend from "../backend" ;
8
- import * as containerCleaner from "../containerCleaner" ;
9
9
import * as planner from "./planner" ;
10
10
import * as fabricator from "./fabricator" ;
11
11
import * as reporter from "./reporter" ;
12
12
import * as executor from "./executor" ;
13
13
import * as prompts from "../prompts" ;
14
- import * as experiments from "../../../experiments" ;
15
14
import { getAppEngineLocation } from "../../../functionsConfig" ;
16
15
import { getFunctionLabel } from "../functionsDeployHelper" ;
17
16
import { FirebaseError } from "../../../error" ;
18
17
import { getProjectNumber } from "../../../getProjectNumber" ;
19
18
import { release as extRelease } from "../../extensions" ;
19
+ import * as artifacts from "../../../functions/artifacts" ;
20
20
21
21
/** Releases new versions of functions and extensions to prod. */
22
22
export async function release (
@@ -104,13 +104,11 @@ export async function release(
104
104
const wantBackend = backend . merge ( ...Object . values ( payload . functions ) . map ( ( p ) => p . wantBackend ) ) ;
105
105
printTriggerUrls ( wantBackend ) ;
106
106
107
- const haveEndpoints = backend . allEndpoints ( wantBackend ) ;
108
- const deletedEndpoints = Object . values ( plan )
109
- . map ( ( r ) => r . endpointsToDelete )
110
- . reduce ( reduceFlat , [ ] ) ;
111
- if ( experiments . isEnabled ( "automaticallydeletegcfartifacts" ) ) {
112
- await containerCleaner . cleanupBuildImages ( haveEndpoints , deletedEndpoints ) ;
113
- }
107
+ await setupArtifactCleanupPolicies (
108
+ options ,
109
+ options . projectId ! ,
110
+ Object . keys ( wantBackend . endpoints ) ,
111
+ ) ;
114
112
115
113
const allErrors = summary . results . filter ( ( r ) => r . error ) . map ( ( r ) => r . error ) as Error [ ] ;
116
114
if ( allErrors . length ) {
@@ -144,3 +142,65 @@ export function printTriggerUrls(results: backend.Backend): void {
144
142
logger . info ( clc . bold ( "Function URL" ) , `(${ getFunctionLabel ( httpsFunc ) } ):` , httpsFunc . uri ) ;
145
143
}
146
144
}
145
+
146
+ /**
147
+ * Sets up artifact cleanup policies for the regions where functions are deployed
148
+ * and automatically sets up policies where needed.
149
+ *
150
+ * The policy is only set up when:
151
+ * 1. No cleanup policy exists yet
152
+ * 2. No other cleanup policies exist (beyond our own if we previously set one)
153
+ * 3. User has not explicitly opted out
154
+ *
155
+ * In non-interactive mode:
156
+ * - With force flag: applies the default cleanup policy
157
+ * - Without force flag: warns and aborts deployment
158
+ */
159
+ async function setupArtifactCleanupPolicies (
160
+ options : Options ,
161
+ projectId : string ,
162
+ locations : string [ ] ,
163
+ ) : Promise < void > {
164
+ if ( locations . length === 0 ) {
165
+ return ;
166
+ }
167
+
168
+ const { locationsToSetup, locationsWithErrors : locationsWithCheckErrors } =
169
+ await artifacts . checkCleanupPolicy ( projectId , locations ) ;
170
+
171
+ if ( locationsToSetup . length === 0 ) {
172
+ return ;
173
+ }
174
+
175
+ const daysToKeep = await prompts . promptForCleanupPolicyDays ( options , locationsToSetup ) ;
176
+
177
+ utils . logLabeledBullet (
178
+ "functions" ,
179
+ `Configuring cleanup policy for ${ locationsToSetup . length > 1 ? "repositories" : "repository" } in ${ locationsToSetup . join ( ", " ) } . ` +
180
+ `Images older than ${ daysToKeep } days will be automatically deleted.` ,
181
+ ) ;
182
+
183
+ const { locationsWithPolicy, locationsWithErrors : locationsWithSetupErrors } =
184
+ await artifacts . setCleanupPolicies ( projectId , locationsToSetup , daysToKeep ) ;
185
+
186
+ utils . logLabeledBullet (
187
+ "functions" ,
188
+ `Configured cleanup policy for ${ locationsWithPolicy . length > 1 ? "repositories" : "repository" } in ${ locationsToSetup . join ( ", " ) } .` ,
189
+ ) ;
190
+
191
+ const locationsWithErrors = [ ...locationsWithCheckErrors , ...locationsWithSetupErrors ] ;
192
+ if ( locationsWithErrors . length > 0 ) {
193
+ utils . logLabeledWarning (
194
+ "functions" ,
195
+ `Failed to set up cleanup policy for repositories in ${ locationsWithErrors . length > 1 ? "regions" : "region" } ` +
196
+ `${ locationsWithErrors . join ( ", " ) } .` +
197
+ "This could result in a small monthly bill as container images accumulate over time." ,
198
+ ) ;
199
+ throw new FirebaseError (
200
+ `Functions successfully deployed but could not set up cleanup policy in ` +
201
+ `${ locationsWithErrors . length > 1 ? "regions" : "region" } ${ locationsWithErrors . join ( ", " ) } .` +
202
+ `Pass the --force option to automatically set up a cleanup policy or` +
203
+ "run 'firebase functions:artifacts:setpolicy' to set up a cleanup policy to automatically delete old images." ,
204
+ ) ;
205
+ }
206
+ }
0 commit comments