Skip to content

Commit

Permalink
Merge branch 'main' into mila/fix-sdk-sort-document-reference-bug
Browse files Browse the repository at this point in the history
  • Loading branch information
milaGGL authored Dec 12, 2024
2 parents 64b2f7f + 50eacb7 commit 85a7adf
Show file tree
Hide file tree
Showing 16 changed files with 403 additions and 69 deletions.
10 changes: 5 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ buildscript {
}

dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.9.4'
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:3.1.0'
classpath 'com.google.gms:google-services:4.3.15'
classpath 'com.google.firebase:firebase-appdistribution-gradle:5.0.0'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
classpath libs.protobuf.gradle.plugin
classpath libs.gradle.errorprone.plugin
classpath libs.google.services
classpath libs.firebase.appdistribution.gradle
classpath libs.firebase.crashlytics.gradle
classpath libs.spotless.plugin.gradle
}
}
Expand Down
25 changes: 24 additions & 1 deletion ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This directory contains tooling used to run Continuous Integration tasks.

## Prerequisites

- Requires python3.5+ and setuptools to be installed.
- Requires python3.9+ and setuptools to be installed.

## Setup

Expand All @@ -22,3 +22,26 @@ This directory contains tooling used to run Continuous Integration tasks.
```
fireci --help
```

## Uninstall

If you run into any issues and need to re-install, or uninstall the package, you can do so
by uninstalling the `fireci` package.

```shell
pip3 uninstall fireci -y
```

## Debug

By default, if you're not running `fireci` within the context of CI, the minimum log level is set
to `INFO`.

To manually set the level to `DEBUG`, you can use the `--debug` flag.

```shell
fireci --debug clean
```

> ![NOTE]
> The `--debug` flag must come _before_ the command.
27 changes: 27 additions & 0 deletions ci/fireci/fireci/ci_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import os
import subprocess

from typing import List, Tuple, Union

_logger = logging.getLogger('fireci.ci_utils')


Expand Down Expand Up @@ -61,3 +63,28 @@ def gcloud_identity_token():
"""Returns an identity token with the current gcloud service account."""
result = subprocess.run(['gcloud', 'auth', 'print-identity-token'], stdout=subprocess.PIPE, check=True)
return result.stdout.decode('utf-8').strip()

def get_projects(file_path: str = "subprojects.cfg") -> List[str]:
"""Parses the specified file for a list of projects in the repo."""
with open(file_path, 'r') as file:
stripped_lines = [line.strip() for line in file]
return [line for line in stripped_lines if line and not line.startswith('#')]

def counts(arr: List[Union[bool, int]]) -> Tuple[int, int]:
"""Given an array of booleans and ints, returns a tuple mapping of [true, false].
Positive int values add to the `true` count while values less than one add to `false`.
"""
true_count = 0
false_count = 0
for value in arr:
if isinstance(value, bool):
if value:
true_count += 1
else:
false_count += 1
elif value >= 1:
true_count += value
else:
false_count += abs(value) if value < 0 else 1

return true_count, false_count
27 changes: 27 additions & 0 deletions ci/fireci/fireci/dir_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
import contextlib
import logging
import os
import pathlib
import shutil
import glob

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

Expand All @@ -30,3 +33,27 @@ def chdir(directory):
finally:
_logger.debug(f'Restoring directory to: {original_dir} ...')
os.chdir(original_dir)

def rmdir(path: str) -> bool:
"""Recursively deletes a directory, and returns a boolean indicating if the dir was deleted."""
dir = pathlib.Path(path)
if not dir.exists():
_logger.debug(f"Directory already deleted: {dir}")
return False

_logger.debug(f"Deleting directory: {dir}")
shutil.rmtree(dir)
return True

def rmglob(pattern: str) -> int:
"""Deletes all files that match a given pattern, and returns the amount of (root) files deleted"""
files = glob.glob(os.path.expanduser(pattern))
for file in files:
path = pathlib.Path(file)
if path.is_dir():
rmdir(file)
else:
_logger.debug(f"Deleting file: {path}")
os.remove(path)

return len(files)
13 changes: 11 additions & 2 deletions ci/fireci/fireci/internal.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ class _CommonOptions:


@click.group()
@click.option(
'--debug/--no-debug',
help='Set the min loglevel to debug.',
default=False
)
@click.option(
'--artifact-target-dir',
default='_artifacts',
Expand All @@ -83,7 +88,7 @@ def main(options, **kwargs):
setattr(options, k, v)


def ci_command(name=None, cls=click.Command, group=main):
def ci_command(name=None, cls=click.Command, group=main, epilog=None):
"""Decorator to use for CI commands.
The differences from the standard @click.command are:
Expand All @@ -94,15 +99,19 @@ def ci_command(name=None, cls=click.Command, group=main):
:param name: Optional name of the task. Defaults to the function name that is decorated with this decorator.
:param cls: Specifies whether the func is a command or a command group. Defaults to `click.Command`.
:param group: Specifies the group the command belongs to. Defaults to the `main` command group.
:param epilog: Specifies epilog text to show at the end of the help text.
"""

def ci_command(f):
actual_name = f.__name__ if name is None else name

@click.command(name=actual_name, cls=cls, help=f.__doc__)
@click.command(name=actual_name, cls=cls, help=f.__doc__, epilog=epilog)
@_pass_options
@click.pass_context
def new_func(ctx, options, *args, **kwargs):
if options.debug:
logging.getLogger('fireci').setLevel(logging.DEBUG)

with _artifact_handler(
options.artifact_target_dir,
options.artifact_patterns,
Expand Down
7 changes: 5 additions & 2 deletions ci/fireci/fireci/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@
from .internal import main

# Unnecessary on CI as GitHub Actions provides them already.
asctime_place_holder = '' if os.getenv('CI') else '%(asctime)s '
is_ci = os.getenv('CI')
asctime_place_holder = '' if is_ci else '%(asctime)s '
log_format = f'[%(levelname).1s] {asctime_place_holder}%(name)s: %(message)s'
logging.basicConfig(
datefmt='%Y-%m-%d %H:%M:%S %z %Z',
format=log_format,
level=logging.INFO,
)
logging.getLogger('fireci').setLevel(logging.DEBUG)

level = logging.DEBUG if is_ci else logging.INFO
logging.getLogger('fireci').setLevel(level)

plugins.discover()

Expand Down
118 changes: 118 additions & 0 deletions ci/fireci/fireciplugins/clean.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import click
import logging

from fireci import ci_command
from fireci import ci_utils
from fireci import dir_utils
from typing import Tuple, List, Callable, Union
from termcolor import colored

log = logging.getLogger('fireci.clean')

@click.argument("projects",
nargs=-1,
type=click.Path(),
required=False
)
@click.option('--gradle/--no-gradle', default=False, help="Delete the local .gradle caches.")
@click.option('--build/--no-build', default=True, help="Delete the local build caches.")
@click.option('--transforms/--no-transforms', default=False, help="Delete the system-wide transforms cache.")
@click.option('--build-cache/--no-build-cache', default=False, help="Delete the system-wide build cache.")

@click.option('--deep/--no-deep', default=False, help="Delete all of the system-wide files for gradle.")
@click.option('--cache/--no-cache', default=False, help="Delete all of the system-wide caches for gradle.")
@ci_command(epilog="""
Clean a subset of projects:
\b
$ fireci clean firebase-common
$ fireci clean firebase-common firebase-vertexai
Clean all projects:
$ fireci clean
""")
def clean(projects, gradle, build, transforms, build_cache, deep, cache):
"""
Delete files cached by gradle.
Alternative to the standard `gradlew clean`, which runs outside the scope of gradle,
and provides deeper cache cleaning capabilities.
"""
if not projects:
log.debug("No projects specified, so we're defaulting to all projects.")
projects = ci_utils.get_projects()

cache = cache or deep
gradle = gradle or cache

cleaners = []

if build:
cleaners.append(delete_build)
if gradle:
cleaners.append(delete_gradle)

results = [call_and_sum(projects, cleaner) for cleaner in cleaners]
local_count = tuple(map(sum, zip(*results)))

cleaners = []

if deep:
cleaners.append(delete_deep)
elif cache:
cleaners.append(delete_cache)
else:
if transforms:
cleaners.append(delete_transforms)
if build_cache:
cleaners.append(delete_build_cache)

results = [cleaner() for cleaner in cleaners]
system_count = ci_utils.counts(results)

[deleted, skipped] = tuple(a + b for a, b in zip(local_count, system_count))

log.info(f"""
Clean results:
{colored("Deleted:", None, attrs=["bold"])} {colored(deleted, "red")}
{colored("Already deleted:", None, attrs=["bold"])} {colored(skipped, "grey")}
""")


def call_and_sum(variables: List[str], func: Callable[[str], Union[bool, int]]) -> Tuple[int, int]:
results = list(map(lambda var: func(var), variables))
return ci_utils.counts(results)

def delete_build(dir: str) -> bool:
return dir_utils.rmdir(f"{dir}/build")

def delete_gradle(dir: str) -> bool:
return dir_utils.rmdir(f"{dir}/.gradle")

def delete_transforms() -> int:
return dir_utils.rmglob("~/.gradle/caches/transforms-*")

def delete_build_cache() -> int:
return dir_utils.rmglob("~/.gradle/caches/build-cache-*")

def delete_deep() -> bool:
return dir_utils.rmdir("~/.gradle")

def delete_cache() -> bool:
return dir_utils.rmdir("~/.gradle/caches")
23 changes: 12 additions & 11 deletions ci/fireci/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ version = 0.1

[options]
install_requires =
protobuf==3.19
click==8.1.3
google-cloud-storage==2.5.0
mypy==0.991
numpy==1.23.1
pandas==1.5.1
PyGithub==1.55
pystache==0.6.0
requests==2.23.0
seaborn==0.12.1
PyYAML==6.0.0
protobuf==3.20.3
click==8.1.7
google-cloud-storage==2.18.2
mypy==1.6.0
numpy==1.24.4
pandas==1.5.3
PyGithub==1.58.2
pystache==0.6.0
requests==2.31.0
seaborn==0.12.2
PyYAML==6.0.1
termcolor==2.4.0

[options.extras_require]
test =
Expand Down
5 changes: 4 additions & 1 deletion firebase-dataconnect/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Unreleased

* [changed] `FirebaseDataConnect.logLevel` type changed from `LogLevel` to
`MutableStateFlow<LogLevel>`. This enables apps to "collect" the flow to,
for example, update a UI component when the log level changes.
([#6586](https://github.com/firebase/firebase-android-sdk/pull/6586))

# 16.0.0-beta03
* [changed] Requires Data Connect emulator version 1.6.1 or later for code generation.
Expand Down
3 changes: 1 addition & 2 deletions firebase-dataconnect/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,7 @@ package com.google.firebase.dataconnect {
public final class FirebaseDataConnectKt {
method @NonNull public static com.google.firebase.dataconnect.FirebaseDataConnect getInstance(@NonNull com.google.firebase.dataconnect.FirebaseDataConnect.Companion, @NonNull com.google.firebase.FirebaseApp app, @NonNull com.google.firebase.dataconnect.ConnectorConfig config, @NonNull com.google.firebase.dataconnect.DataConnectSettings settings = com.google.firebase.dataconnect.DataConnectSettings());
method @NonNull public static com.google.firebase.dataconnect.FirebaseDataConnect getInstance(@NonNull com.google.firebase.dataconnect.FirebaseDataConnect.Companion, @NonNull com.google.firebase.dataconnect.ConnectorConfig config, @NonNull com.google.firebase.dataconnect.DataConnectSettings settings = com.google.firebase.dataconnect.DataConnectSettings());
method @NonNull public static com.google.firebase.dataconnect.LogLevel getLogLevel(@NonNull com.google.firebase.dataconnect.FirebaseDataConnect.Companion);
method public static void setLogLevel(@NonNull com.google.firebase.dataconnect.FirebaseDataConnect.Companion, @NonNull com.google.firebase.dataconnect.LogLevel);
method @NonNull public static kotlinx.coroutines.flow.MutableStateFlow<com.google.firebase.dataconnect.LogLevel> getLogLevel(@NonNull com.google.firebase.dataconnect.FirebaseDataConnect.Companion);
}

@kotlinx.serialization.Serializable(with=LocalDateSerializer::class) public final class LocalDate {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.google.firebase.app
import com.google.firebase.dataconnect.core.FirebaseDataConnectFactory
import com.google.firebase.dataconnect.core.LoggerGlobals
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.SerializationStrategy
import kotlinx.serialization.modules.SerializersModule
Expand Down Expand Up @@ -381,8 +382,14 @@ public fun FirebaseDataConnect.Companion.getInstance(
/**
* The log level used by all [FirebaseDataConnect] instances.
*
* As a [MutableStateFlow], the log level can be changed by assigning [MutableStateFlow.value].
* Also, the flow can be "collected" as a means of observing the log level, which may be useful in
* the case that a user interface shows a UI element, such as a checkbox, to represent whether debug
* logging is enabled.
*
* The default log level is [LogLevel.WARN]. Setting this to [LogLevel.DEBUG] will enable debug
* logging, which is especially useful when reporting issues to Google or investigating problems
* yourself. Setting it to [LogLevel.NONE] will disable all logging.
*/
public var FirebaseDataConnect.Companion.logLevel: LogLevel by LoggerGlobals::logLevel
public val FirebaseDataConnect.Companion.logLevel: MutableStateFlow<LogLevel>
get() = LoggerGlobals.logLevel
Loading

0 comments on commit 85a7adf

Please sign in to comment.