Skip to content

Commit 3733ca0

Browse files
committed
Implement clean command
1 parent 04ab616 commit 3733ca0

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

ci/fireci/fireci/ci_utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,9 @@ def gcloud_identity_token():
6161
"""Returns an identity token with the current gcloud service account."""
6262
result = subprocess.run(['gcloud', 'auth', 'print-identity-token'], stdout=subprocess.PIPE, check=True)
6363
return result.stdout.decode('utf-8').strip()
64+
65+
def get_projects(file_path: str = "subprojects.cfg") -> list[str]:
66+
"""Parses the specified file for a list of projects in the repo."""
67+
with open(file_path, 'r') as file:
68+
stripped_lines = [line.strip() for line in file]
69+
return [line for line in stripped_lines if line and not line.startswith('#')]

ci/fireci/fireci/dir_utils.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
import contextlib
1616
import logging
1717
import os
18+
import pathlib
19+
import shutil
20+
import glob
1821

1922
_logger = logging.getLogger('fireci.dir_utils')
2023

@@ -30,3 +33,28 @@ def chdir(directory):
3033
finally:
3134
_logger.debug(f'Restoring directory to: {original_dir} ...')
3235
os.chdir(original_dir)
36+
37+
def rmdir(path: str) -> bool:
38+
"""Recursively deletes a directory, and returns a boolean indicating if the dir was deleted."""
39+
dir = pathlib.Path(path)
40+
if not dir.exists():
41+
_logger.debug(f"Directory already deleted: {dir}")
42+
return False
43+
44+
_logger.debug(f"Deleting directory: {dir}")
45+
shutil.rmtree(dir)
46+
return True
47+
48+
def rmglob(pattern: str) -> int:
49+
"""Deletes all files that match a given pattern, and returns the amount of (root) files deleted"""
50+
files = 0
51+
for file in glob.glob(os.path.expanduser(pattern)):
52+
path = pathlib.Path(file)
53+
if path.is_dir():
54+
rmdir(path)
55+
else:
56+
_logger.debug(f"Deleting file: {path}")
57+
os.remove(path)
58+
files += 1
59+
60+
return files

ci/fireci/fireciplugins/clean.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# Copyright 2024 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import click
16+
import logging
17+
18+
from fireci import ci_command
19+
from fireci import ci_utils
20+
from fireci import dir_utils
21+
22+
log = logging.getLogger('fireci.clean')
23+
24+
@click.argument("projects",
25+
nargs=-1,
26+
type=click.Path(),
27+
required=False
28+
)
29+
@click.option('--gradle/--no-gradle', default=False, help="Delete the local .gradle caches.")
30+
@click.option('--build/--no-build', default=True, help="Delete the local build caches.")
31+
@click.option('--transforms/--no-transforms', default=False, help="Delete the system-wide transforms cache.")
32+
@click.option('--build-cache/--no-build-cache', default=False, help="Delete the system-wide build cache.")
33+
34+
@click.option('--deep/--no-deep', default=False, help="Delete all of the system-wide files for gradle.")
35+
@click.option('--cache/--no-cache', default=False, help="Delete all of the system-wide caches for gradle.")
36+
@ci_command(epilog="""
37+
Clean a subset of projects:
38+
39+
\b
40+
$ fireci clean firebase-common
41+
$ fireci clean firebase-common firebase-vertexai
42+
43+
Clean all projects:
44+
45+
$ fireci clean
46+
""")
47+
def clean(projects, gradle, build, transforms, build_cache, deep, cache):
48+
"""
49+
Delete files cached by gradle.
50+
51+
Alternative to the standard `gradlew clean`, which runs outside the scope of gradle,
52+
and provides deeper cache cleaning capabilities.
53+
"""
54+
if not projects:
55+
log.debug("No projects specified, so we're defaulting to all projects.")
56+
projects = ci_utils.get_projects()
57+
58+
cache = cache or deep
59+
gradle = gradle or cache
60+
61+
cleaners = []
62+
63+
if build:
64+
cleaners.append(delete_build)
65+
if gradle:
66+
cleaners.append(delete_build)
67+
68+
results = [call_and_sum(projects, cleaner) for cleaner in cleaners]
69+
delete_count = sum(map(int, results))
70+
71+
cleaners = []
72+
73+
if deep:
74+
cleaners.append(delete_deep)
75+
elif cache:
76+
cleaners.append(delete_cache)
77+
else:
78+
if transforms:
79+
cleaners.append(delete_transforms)
80+
if build_cache:
81+
cleaners.append(delete_build_cache)
82+
83+
results = [cleaner() for cleaner in cleaners]
84+
delete_count += sum(map(int, results))
85+
86+
log.info(f"Deleted {delete_count} directories/files")
87+
88+
def call_and_sum(variables, func) -> int:
89+
results = map(lambda var: func(var), variables)
90+
return sum(map(int, results))
91+
92+
def delete_build(dir: str) -> bool:
93+
return dir_utils.rmdir(f"{dir}/build")
94+
95+
def delete_gradle(dir: str) -> bool:
96+
return dir_utils.rmdir(f"{dir}/.gradle")
97+
98+
def delete_transforms() -> int:
99+
return dir_utils.rmglob("~/.gradle/caches/transforms-*")
100+
101+
def delete_build_cache() -> int:
102+
return dir_utils.rmglob("~/.gradle/caches/build-cache-*")
103+
104+
def delete_deep() -> bool:
105+
return dir_utils.rmdir("~/.gradle")
106+
107+
def delete_cache() -> bool:
108+
return dir_utils.rmdir("~/.gradle/caches")

0 commit comments

Comments
 (0)