aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base_updater.py7
-rw-r--r--crates_updater.py21
-rw-r--r--external_updater.py101
-rw-r--r--external_updater_reviewers_test.py7
-rw-r--r--git_utils.py10
-rw-r--r--metadata.proto45
-rwxr-xr-xregen_bp.sh5
-rw-r--r--reviewers.py13
-rw-r--r--update_package.sh26
-rwxr-xr-xupdater.sh2
-rw-r--r--updater_utils.py5
11 files changed, 160 insertions, 82 deletions
diff --git a/base_updater.py b/base_updater.py
index 18d4435..78dde1c 100644
--- a/base_updater.py
+++ b/base_updater.py
@@ -49,6 +49,13 @@ class Updater:
"""
raise NotImplementedError()
+ def rollback(self) -> bool:
+ """Rolls the current update back.
+
+ This is an optional operation. Returns whether the rollback succeeded.
+ """
+ return False
+
@property
def project_path(self) -> Path:
"""Gets absolute path to the project."""
diff --git a/crates_updater.py b/crates_updater.py
index 8207375..ee476b5 100644
--- a/crates_updater.py
+++ b/crates_updater.py
@@ -18,6 +18,8 @@ import os
# pylint: disable=g-importing-member
from pathlib import Path
import re
+import shutil
+import tempfile
import urllib.request
import archive_utils
@@ -48,6 +50,8 @@ class CratesUpdater(Updater):
download_url: str
package: str
+ package_dir: str
+ temp_file: tempfile.NamedTemporaryFile
def is_supported_url(self) -> bool:
if self._old_url.type != metadata_pb2.URL.HOMEPAGE:
@@ -115,12 +119,25 @@ class CratesUpdater(Updater):
"""
try:
temporary_dir = archive_utils.download_and_extract(self.download_url)
- package_dir = archive_utils.find_archive_root(temporary_dir)
- updater_utils.replace_package(package_dir, self._proj_path)
+ self.package_dir = archive_utils.find_archive_root(temporary_dir)
+ self.temp_file = tempfile.NamedTemporaryFile()
+ updater_utils.replace_package(self.package_dir, self._proj_path,
+ self.temp_file.name)
self.check_for_errors()
finally:
urllib.request.urlcleanup()
+ def rollback(self) -> bool:
+ # Only rollback if we have already swapped,
+ # which we denote by writing to this file.
+ if os.fstat(self.temp_file.fileno()).st_size > 0:
+ tmp_dir = tempfile.TemporaryDirectory()
+ shutil.move(self._proj_path, tmp_dir.name)
+ shutil.move(self.package_dir, self._proj_path)
+ shutil.move(Path(tmp_dir.name) / self.package, self.package_dir)
+ return True
+ return False
+
# pylint: disable=no-self-use
def update_metadata(self, metadata: metadata_pb2.MetaData,
full_path: Path) -> None:
diff --git a/external_updater.py b/external_updater.py
index 4d40fa0..c15e92d 100644
--- a/external_updater.py
+++ b/external_updater.py
@@ -22,6 +22,7 @@ updater.sh update --refresh --keep_date rust/crates/libc
import argparse
import enum
+import glob
import json
import os
import sys
@@ -94,25 +95,32 @@ def _do_update(args: argparse.Namespace, updater: Updater,
git_utils.checkout(full_path, args.remote_name + '/master')
git_utils.start_branch(full_path, TMP_BRANCH_NAME)
- updater.update()
-
- updated_metadata = metadata_pb2.MetaData()
- updated_metadata.CopyFrom(metadata)
- updated_metadata.third_party.version = updater.latest_version
- for metadata_url in updated_metadata.third_party.url:
- if metadata_url == updater.current_url:
- metadata_url.CopyFrom(updater.latest_url)
- # For Rust crates, replace GIT url with ARCHIVE url
- if isinstance(updater, CratesUpdater):
- updater.update_metadata(updated_metadata, full_path)
- fileutils.write_metadata(full_path, updated_metadata, args.keep_date)
- git_utils.add_file(full_path, 'METADATA')
-
- if args.branch_and_commit:
- msg = 'Upgrade {} to {}\n\nTest: make\n'.format(
- args.path, updater.latest_version)
- git_utils.add_file(full_path, '*')
- git_utils.commit(full_path, msg)
+ try:
+ updater.update()
+
+ updated_metadata = metadata_pb2.MetaData()
+ updated_metadata.CopyFrom(metadata)
+ updated_metadata.third_party.version = updater.latest_version
+ for metadata_url in updated_metadata.third_party.url:
+ if metadata_url == updater.current_url:
+ metadata_url.CopyFrom(updater.latest_url)
+ # For Rust crates, replace GIT url with ARCHIVE url
+ if isinstance(updater, CratesUpdater):
+ updater.update_metadata(updated_metadata, full_path)
+ fileutils.write_metadata(full_path, updated_metadata, args.keep_date)
+ git_utils.add_file(full_path, 'METADATA')
+
+ if args.branch_and_commit:
+ rel_proj_path = fileutils.get_relative_project_path(full_path)
+ msg = 'Upgrade {} to {}\n\nTest: make\n'.format(
+ rel_proj_path, updater.latest_version)
+ git_utils.remove_gitmodules(full_path)
+ git_utils.add_file(full_path, '*')
+ git_utils.commit(full_path, msg)
+ except Exception as err:
+ if updater.rollback():
+ print('Rolled back.')
+ raise err
if args.push_change:
git_utils.push(full_path, args.remote_name, updater.has_errors)
@@ -162,12 +170,13 @@ def check_and_update(args: argparse.Namespace,
return str(err)
-def _check_path(args: argparse.Namespace, paths: Iterator[str],
- delay: int) -> Dict[str, Dict[str, str]]:
+def check_and_update_path(args: argparse.Namespace, paths: Iterator[str],
+ update_lib: bool,
+ delay: int) -> Dict[str, Dict[str, str]]:
results = {}
for path in paths:
res = {}
- updater = check_and_update(args, Path(path))
+ updater = check_and_update(args, Path(path), update_lib)
if isinstance(updater, str):
res['error'] = updater
else:
@@ -188,19 +197,45 @@ def _list_all_metadata() -> Iterator[str]:
dirs.sort(key=lambda d: d.lower())
+def get_paths(paths: List[str]) -> List[str]:
+ """Expand paths via globs."""
+ # We want to use glob to get all the paths, so we first convert to absolute.
+ abs_paths = [fileutils.get_absolute_project_path(Path(path))
+ for path in paths]
+ result = [path for abs_path in abs_paths
+ for path in sorted(glob.glob(str(abs_path)))]
+ if paths and not result:
+ print('Could not find any valid paths in %s' % str(paths))
+ return result
+
+
+def write_json(json_file: str, results: Dict[str, Dict[str, str]]) -> List[str]:
+ """Output a JSON report."""
+ with Path(json_file).open('w') as res_file:
+ json.dump(results, res_file, sort_keys=True, indent=4)
+
+
def check(args: argparse.Namespace) -> None:
"""Handler for check command."""
- paths = _list_all_metadata() if args.all else args.paths
- results = _check_path(args, paths, args.delay)
+ paths = _list_all_metadata() if args.all else get_paths(args.paths)
+ results = check_and_update_path(args, paths, False, args.delay)
if args.json_output is not None:
- with Path(args.json_output).open('w') as res_file:
- json.dump(results, res_file, sort_keys=True, indent=4)
+ write_json(args.json_output, results)
def update(args: argparse.Namespace) -> None:
"""Handler for update command."""
- check_and_update(args, args.path, update_lib=True)
+ all_paths = get_paths(args.paths)
+ # Remove excluded paths.
+ excludes = set() if args.exclude is None else set(args.exclude)
+ filtered_paths = [path for path in all_paths
+ if not Path(path).name in excludes]
+ # Now we can update each path.
+ results = check_and_update_path(args, filtered_paths, True, 0)
+
+ if args.json_output is not None:
+ write_json(args.json_output, results)
def parse_args() -> argparse.Namespace:
@@ -235,9 +270,12 @@ def parse_args() -> argparse.Namespace:
# Creates parser for update command.
update_parser = subparsers.add_parser('update', help='Update one project.')
update_parser.add_argument(
- 'path',
- help='Path of the project. '
+ 'paths',
+ nargs='*',
+ help='Paths of the project as globs. '
'Relative paths will be resolved from external/.')
+ update_parser.add_argument('--json_output',
+ help='Path of a json file to write result to.')
update_parser.add_argument(
'--force',
help='Run update even if there\'s no new version.',
@@ -260,6 +298,11 @@ def parse_args() -> argparse.Namespace:
default='aosp',
required=False,
help='Upstream remote name.')
+ update_parser.add_argument('--exclude',
+ action='append',
+ help='Names of projects to exclude. '
+ 'These are just the final part of the path '
+ 'with no directories.')
update_parser.set_defaults(func=update)
return parser.parse_args()
diff --git a/external_updater_reviewers_test.py b/external_updater_reviewers_test.py
index d6e4b71..18497f5 100644
--- a/external_updater_reviewers_test.py
+++ b/external_updater_reviewers_test.py
@@ -28,7 +28,6 @@ class ExternalUpdaterReviewersTest(unittest.TestCase):
self.saved_proj_reviewers = reviewers.PROJ_REVIEWERS
self.saved_rust_reviewers = reviewers.RUST_REVIEWERS
self.saved_rust_reviewer_list = reviewers.RUST_REVIEWER_LIST
- self.saved_num_rust_projects = reviewers.NUM_RUST_PROJECTS
self.saved_rust_crate_owners = reviewers.RUST_CRATE_OWNERS
def tearDown(self):
@@ -37,7 +36,6 @@ class ExternalUpdaterReviewersTest(unittest.TestCase):
reviewers.PROJ_REVIEWERS = self.saved_proj_reviewers
reviewers.RUST_REVIEWERS = self.saved_rust_reviewers
reviewers.RUST_REVIEWER_LIST = self.saved_rust_reviewer_list
- reviewers.NUM_RUST_PROJECTS = self.saved_num_rust_projects
reviewers.RUST_CRATE_OWNERS = self.saved_rust_crate_owners
# pylint: disable=no-self-use
@@ -74,16 +72,11 @@ class ExternalUpdaterReviewersTest(unittest.TestCase):
"""Check the constants associated to the reviewers."""
# There should be enough people in the reviewers pool.
self.assertGreaterEqual(len(reviewers.RUST_REVIEWERS), 3)
- # The NUM_RUST_PROJECTS should not be too small.
- self.assertGreaterEqual(reviewers.NUM_RUST_PROJECTS, 50)
- self.assertGreaterEqual(reviewers.NUM_RUST_PROJECTS,
- len(reviewers.RUST_CRATE_OWNERS))
# Assume no project reviewers and recreate RUST_REVIEWER_LIST
reviewers.PROJ_REVIEWERS = {}
reviewers.RUST_REVIEWER_LIST = reviewers.create_rust_reviewer_list()
sum_projects = sum(reviewers.RUST_REVIEWERS.values())
self.assertEqual(sum_projects, len(reviewers.RUST_REVIEWER_LIST))
- self.assertGreaterEqual(sum_projects, reviewers.NUM_RUST_PROJECTS)
def test_reviewers_randomness(self):
"""Check random selection of reviewers."""
diff --git a/git_utils.py b/git_utils.py
index f96a600..5186eb3 100644
--- a/git_utils.py
+++ b/git_utils.py
@@ -142,8 +142,9 @@ def merge(proj_path: Path, branch: str) -> None:
"""Merges a branch."""
try:
_run(['git', 'merge', branch, '--no-commit'], cwd=proj_path)
- except subprocess.CalledProcessError:
- # Merge failed. Error is already written to console.
+ except subprocess.CalledProcessError as err:
+ if hasattr(err, "output"):
+ print(err.output)
_run(['git', 'merge', '--abort'], cwd=proj_path)
raise
@@ -153,6 +154,11 @@ def add_file(proj_path: Path, file_name: str) -> None:
_run(['git', 'add', file_name], cwd=proj_path)
+def remove_gitmodules(proj_path: Path) -> None:
+ """Deletes .gitmodules files."""
+ _run(['find', '.', '-name', '.gitmodules', '-delete'], cwd=proj_path)
+
+
def delete_branch(proj_path: Path, branch_name: str) -> None:
"""Force delete a branch."""
_run(['git', 'branch', '-D', branch_name], cwd=proj_path)
diff --git a/metadata.proto b/metadata.proto
index f517021..ed72c34 100644
--- a/metadata.proto
+++ b/metadata.proto
@@ -1,4 +1,4 @@
-// copyright (C) 2018 The Android Open Source Project
+// Copyright (C) 2018 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -17,14 +17,17 @@
// This proto will only contain fields and values the updater cares about.
// It is not intended to be the formal definition of METADATA file.
-syntax = "proto3";
+// See google3/third_party/metadata.proto if you need to add more stuff to match
+// upstream.
+
+syntax = "proto2"; // As long as upstream is proto2...
package external_updater;
message MetaData {
- string name = 1;
- string description = 3;
- ThirdPartyMetaData third_party = 13;
+ optional string name = 1;
+ optional string description = 3;
+ optional ThirdPartyMetaData third_party = 13;
}
enum LicenseType {
@@ -40,10 +43,11 @@ enum LicenseType {
message ThirdPartyMetaData {
repeated URL url = 1;
- string version = 2;
- LicenseType license_type = 4;
- string license_note = 5;
- Date last_upgrade_date = 10;
+ optional string version = 2;
+ optional LicenseType license_type = 4;
+ optional string license_note = 5;
+ optional Security security = 7;
+ optional Date last_upgrade_date = 10;
}
message URL {
@@ -58,13 +62,26 @@ message URL {
OTHER = 11;
}
- Type type = 1;
+ optional Type type = 1;
- string value = 2;
+ optional string value = 2;
}
message Date {
- int32 year = 1;
- int32 month = 2;
- int32 day = 3;
+ optional int32 year = 1;
+ optional int32 month = 2;
+ optional int32 day = 3;
+}
+
+message Security {
+ enum Category {
+ SANDBOXED_ONLY = 1;
+ TRUSTED_DATA_ONLY = 2;
+ REVIEWED_AND_SECURE = 3;
+ }
+
+ optional Category category = 1;
+ optional string note = 2;
+ repeated string tag = 3;
+ repeated string mitigated_security_patch = 5;
}
diff --git a/regen_bp.sh b/regen_bp.sh
index c84ff53..ddeab8a 100755
--- a/regen_bp.sh
+++ b/regen_bp.sh
@@ -26,14 +26,15 @@ set -e
# Wrapper around cargo2android.
C2A_WRAPPER="/google/bin/releases/android-rust/cargo2android/sandbox.par"
+C2A_WRAPPER_FLAGS="--updater"
function main() {
check_files $*
update_files_with_cargo_pkg_vars
# Save Cargo.lock if it existed before this update.
[ ! -f Cargo.lock ] || mv Cargo.lock Cargo.lock.saved
- echo "Updating Android.bp: $C2A_WRAPPER -- $FLAGS"
- $C2A_WRAPPER -- $FLAGS
+ echo "Updating Android.bp: $C2A_WRAPPER $C2A_WRAPPER_FLAGS -- $FLAGS"
+ $C2A_WRAPPER $C2A_WRAPPER_FLAGS -- $FLAGS
copy_cargo_out_files $*
rm -rf target.tmp cargo.out Cargo.lock
# Restore Cargo.lock if it existed before this update.
diff --git a/reviewers.py b/reviewers.py
index 6f826f1..39a7349 100644
--- a/reviewers.py
+++ b/reviewers.py
@@ -31,6 +31,7 @@ ProjMapping = Mapping[str, Union[str, List[str], Set[str]]]
RUST_CRATE_OWNERS: ProjMapping = {
'rust/crates/anyhow': 'mmaurer@google.com',
# more rust crate owners could be added later
+ # if so, consider modifying the quotas in RUST_REVIEWERS
}
PROJ_REVIEWERS: ProjMapping = {
@@ -40,20 +41,14 @@ PROJ_REVIEWERS: ProjMapping = {
# Combine all roject reviewers.
PROJ_REVIEWERS.update(RUST_CRATE_OWNERS)
-# Estimated number of rust projects, not the actual number.
-# It is only used to make random distribution "fair" among RUST_REVIEWERS.
-# It should not be too small, to spread nicely to multiple reviewers.
-# It should be larger or equal to len(RUST_CRATES_OWNERS).
-NUM_RUST_PROJECTS = 120
-
# Reviewers for external/rust/crates projects not found in PROJ_REVIEWER.
# Each person has a quota, the number of projects to review.
-# Sum of these numbers should be greater or equal to NUM_RUST_PROJECTS
-# to avoid error cases in the creation of RUST_REVIEWER_LIST.
+# The sum of these quotas should ideally be at least the number of Rust
+# projects, but this only matters if we have many entries in RUST_CRATE_OWNERS,
+# as we subtract a person's owned crates from their quota.
RUST_REVIEWERS: Mapping[str, int] = {
'ivanlozano@google.com': 20,
'jeffv@google.com': 20,
- 'jgalenson@google.com': 20,
'mmaurer@google.com': 20,
'srhines@google.com': 20,
'tweek@google.com': 20,
diff --git a/update_package.sh b/update_package.sh
index 219c566..4715db7 100644
--- a/update_package.sh
+++ b/update_package.sh
@@ -21,6 +21,7 @@ set -e
tmp_dir=$1
external_dir=$2
+tmp_file=$3
# root of Android source tree
root_dir=`pwd`
@@ -45,19 +46,14 @@ CopyIfPresent "METADATA"
CopyIfPresent "TEST_MAPPING"
CopyIfPresent ".git"
CopyIfPresent ".gitignore"
-CopyIfPresent "cargo2android.json"
+if compgen -G "$external_dir/cargo2android*"; then
+ cp -a -f -n $external_dir/cargo2android* .
+fi
CopyIfPresent "patches"
CopyIfPresent "post_update.sh"
CopyIfPresent "OWNERS"
CopyIfPresent "README.android"
-if [ -f $tmp_dir/Cargo.toml -a -f $tmp_dir/Android.bp ]
-then
- # regenerate Android.bp before local patches, so it is
- # possible to patch the generated Android.bp after this.
- /bin/bash `dirname $0`/regen_bp.sh $root_dir $external_dir
-fi
-
echo "Applying patches..."
for p in $tmp_dir/patches/*.{diff,patch}
do
@@ -87,13 +83,15 @@ then
fi
echo "Swapping old and new..."
-rm -rf $external_dir
+second_tmp_dir=`mktemp -d`
+mv $external_dir $second_tmp_dir
mv $tmp_dir $external_dir
-
-echo "Updating TEST_MAPPING..."
-UCT="$root_dir/development/scripts/update_crate_tests.py"
-[ -f "$UCT" ] || abort "ERROR: cannot find $UCT"
-$UCT $external_dir
+mv $second_tmp_dir/* $tmp_dir
+rm -rf $second_tmp_dir
+if [ -n "$tmp_file" ]; then
+ # Write to the temporary file to show we have swapped.
+ echo "Swapping" > $tmp_file
+fi
cd $external_dir
git add .
diff --git a/updater.sh b/updater.sh
index b8078c6..5aca93d 100755
--- a/updater.sh
+++ b/updater.sh
@@ -20,4 +20,4 @@ cd $(dirname "$0")/../..
source build/envsetup.sh
lunch aosp_arm-eng
mmma tools/external_updater
-out/soong/host/linux-x86/bin/external_updater $@
+out/host/linux-x86/bin/external_updater $@
diff --git a/updater_utils.py b/updater_utils.py
index 1bcd567..209a1cd 100644
--- a/updater_utils.py
+++ b/updater_utils.py
@@ -48,7 +48,7 @@ def create_updater(metadata: metadata_pb2.MetaData, proj_path: Path,
raise ValueError('No supported URL.')
-def replace_package(source_dir, target_dir) -> None:
+def replace_package(source_dir, target_dir, temp_file=None) -> None:
"""Invokes a shell script to prepare and update a project.
Args:
@@ -59,7 +59,8 @@ def replace_package(source_dir, target_dir) -> None:
print('Updating {} using {}.'.format(target_dir, source_dir))
script_path = os.path.join(os.path.dirname(sys.argv[0]),
'update_package.sh')
- subprocess.check_call(['bash', script_path, source_dir, target_dir])
+ subprocess.check_call(['bash', script_path, source_dir, target_dir,
+ "" if temp_file is None else temp_file])
VERSION_SPLITTER_PATTERN: str = r'[\.\-_]'