Merge branch 'stable-3.0'

* stable-3.0:
  Update git submodules
  Update git submodules
  Update git submodules
  Fix documentation of UI selection
  Set version to 2.16.12-SNAPSHOT
  Set version to 2.16.11
  Update git submodules
  Revert "GerritServer: Silence non-critical logs from JGit's FileSnapshot"
  Revert "GerritServer: Silence non-critical logs from JGit's FS"
  Revert "Upgrade JGit to 5.1.10.201908230655-r"
  Revert "Stop using deprecated DirCacheEntry#setLastModified(long)"
  Update git submodules
  Update git submodules
  Update git submodules
  Update git submodules
  Update git submodules
  Update git submodules
  Set version to 2.15.17-SNAPSHOT
  Update git submodules
  Update git submodules
  Revert "Upgrade JGit to 5.3.4.201908231101-r"
  Set version to 2.15.16
  Revert "CreateAccount: Fail early when invalid SSH key is given"
  Update git submodules
  tools/eclipse/project.py: Fix typo of bazelisk
  Set XSRF on '/' under PolyGerrit
  Migrate from old-style legacy .java provider to the new JavaInfo.
  Remove deleted rules for the new added section
  Support bazelisk or bazel in tools/eclipse/project.py
  Rework imports in project.py
  Update project.py to use argparse
  AccountIT: Test that account is not created with invalid email
  CreateAccount: Fail early when invalid SSH key is given
  Update git submodules
  GerritServer: Silence non-critical logs from JGit's FS
  GerritServer: Silence non-critical logs from JGit's FileSnapshot
  Stop using deprecated DirCacheEntry#setLastModified(long)
  ProjectState: Fix 'invalid type qualification' javadoc warning
  Upgrade JGit to 5.3.4.201908231101-r
  Documentation: refresh IntelliJ IDEA developer documentation
  Documentation: update external links to Bazel
  Use base url for commentlink
  Update git submodules
  Update git submodules
  Don't store LabelTypes in ProjectState
  Upgrade highlight.js to latest master revision
  Update git submodules
  Update git submodules
  Update git submodules
  StarredChangesUtil: Stop using deprecated RefDatabase.getRef
  Suppress warnings about deprecated BaseReceivePack
  DeleteRef: Stop using deprecated RefDatabase.getRef
  Upgrade elasticsearch-rest-client to 7.3.1
  Add .gitreview file
  Remove vestigal GWT plugin loading hooks
  Upgrade JGit to 5.1.10.201908230655-r
  DeleteDraftComments: Don't update change modified timestamp
  ChangeIT: set submittableAfterLosingPermissions private
  Rebase: Don't swallow caught exception
  Output NoteDb migration progress to Flogger
  Update git submodules
  Remove AccountPatchReview data when change gets auto-abandoned
  DefaultChangeReportFormatter: Make constructor and urlFormatter visible
  StarredChangesUtil: Fix NPE when ref to be deleted doesn't exist
  StarredChangesUtil: Throw LockFailureException on LOCK_FAILURE
  Add test for creating a change on a non-existing base change
  Rebase: Do not fail with 500 ISE if non-existing change is given as base
  Fix detecting changes of parent trees when computing change kind for merge commit
  Remove duplicate descriptions of fields in Requirement JSON entity
  Allow to set content type in the plugin REST API interface
  InternalAccountQuery: Add back the oneByExternalId method
  Set version to 2.16.11-SNAPSHOT
  Revert "Migrate from old-style legacy .java provider to the new JavaInfo."
  Catch all exceptions for reporting on Schema_130 migration
  Update git submodules
  Migrate from old-style legacy .java provider to the new JavaInfo.
  Update git submodules
  Update git submodules
  AuthRequest: Fix Javadoc for return values
  ChangeApi: Add methods to get change comments/drafts as list
  Update git submodules
  ListChangeComments: Extend ListChangeDrafts
  Upgrade Go Bazel rules to the latest version
  detach -> detached
  Fix anchor tag for settings page
  Add support for Elasticsearch version 7.3.*
  PrologEnvironment: Reduce "setting reductionLimit" log spam
  ElasticContainer: Upgrade to 6.8.2 image for V6_8 tests
  Fix typo: program
  Fix email token routing
  Clarify usage of 'parent' option in list files API
  Remove unused Skylark patch file
  Files: Use Gerrit API to get revision parents
  Fix broken link for rest-api-projects.html#commentlink-info
  Add support for Elasticsearch version 6.8.x
  Upgrade elasticsearch-rest-client to 7.2.1
  Add support for "Link Another Identity" in gr-identities
  Update git submodules
  CommitApi: Add method to get commit info
  Consolidate all CommitApi tests into a single class
  Files: Validate parent option to prevent internal server error
  RevisionIT: Assert that files(base) only works for patch set revisions
  Fix and expand documentation of REST API to get revision files
  RevisionIT#files: Simplify assertion
  Update git submodules
  Update git submodules
  Update git submodules
  Update git submodules
  Remove default bug tracker from _feedbackUrl
  PG: Add shortcuts for dashboard and watched changes
  PG: Allow empty label values
  Remove token param from getCapabilities
  Add an extension point to show a small banner next to the search bar
  Fix gr-group-audit-log to use tbody

Change-Id: Iaf877b737f115d4fe95af74f8284eec19112a21d
This commit is contained in:
David Pursehouse 2019-08-30 10:55:05 +09:00
commit 923318b446
60 changed files with 1039 additions and 504 deletions

View File

@ -1,6 +1,6 @@
# The project view file (.bazelproject) is used to import Gerrit Bazel packages into the IDE.
#
# See: https://ij.bazel.io/docs/project-views.html
# See: https://ij.bazel.build/docs/project-views.html
directories:
.

5
.gitreview Normal file
View File

@ -0,0 +1,5 @@
[gerrit]
host=gerrit-review.googlesource.com
scheme=https
project=gerrit.git
defaultbranch=master

View File

@ -1389,6 +1389,13 @@ Whether changes which are mergeable should be auto-abandoned.
+
By default `true`.
[[changeCleanup.cleanupAccountPatchReview]]changeCleanup.cleanupAccountPatchReview::
+
Whether accountPatchReview data should be also removed when change
gets auto-abandoned.
+
By default `false`.
[[changeCleanup.abandonMessage]]changeCleanup.abandonMessage::
+
Change message that should be posted when a change is abandoned.

View File

@ -10,7 +10,7 @@ To build Gerrit from source, you need:
* Python 2 or 3
* link:https://github.com/nodesource/distributions/blob/master/README.md[Node.js (including npm)]
* Bower (`sudo npm install -g bower`)
* link:https://www.bazel.io/versions/master/docs/install.html[Bazel]
* link:https://docs.bazel.build/versions/master/install.html[Bazel]
* Maven
* zip, unzip
* gcc
@ -225,7 +225,7 @@ is not regenerated.
=== IntelliJ
The Gerrit build works with Bazel's link:https://ij.bazel.io[IntelliJ plugin].
The Gerrit build works with Bazel's link:https://ij.bazel.build[IntelliJ plugin].
Please follow the instructions on <<dev-intellij#,IntelliJ Setup>>.
=== Eclipse
@ -245,7 +245,7 @@ and then follow the link:dev-eclipse.html#setup[setup instructions].
If an updated classpath is needed, the Eclipse project can be
refreshed and missing dependency JARs can be downloaded by running
`project.py` again. For IntelliJ, you need to click the `Sync Project
with BUILD Files` button of link:https://ij.bazel.io[IntelliJ plugin].
with BUILD Files` button of link:https://ij.bazel.build[Bazel plugin].
[[documentation]]
=== Documentation

View File

@ -88,6 +88,11 @@ by clicking the 'Obtain Password' link on the
link:https://gerrit-review.googlesource.com/#/settings/http-password[HTTP
Password tab of the user settings page].
Alternately, you may use the
link:https://pypi.org/project/git-review/[git-review] tool to submit changes
to Gerrit. If you do, it will set up the Change-Id hook and `gerrit` remote
for you. You will still need to do the HTTP access step.
[[style]]
== Style

View File

@ -1,10 +1,24 @@
= Gerrit Code Review - IntelliJ Setup
= Gerrit Code Review - IntelliJ IDEA Setup
== Prerequisites
You need an installation of IntelliJ version 2016.2 or later. The latest version
might not yet be in-sync with the Bazel plugin for IntelliJ. It usually becomes
so quite quickly after new IDEA versions get released, though. It should then be
possible to use the fairly latest IntelliJ release with an updated Bazel plugin.
=== Bazel
Bazel must be installed as described by
<<dev-bazel#installation,Building with Bazel - Installation>>.
It's strongly recommended to verify you can build your Gerrit tree with Bazel
for Java 8 from the command line first. Ensure that at least
`bazel build gerrit` runs successfully before you proceed.
=== IntelliJ version and Bazel plugin
Before downloading IntelliJ, look at the
link:https://plugins.jetbrains.com/plugin/8609-bazel/versions[JetBrains plugin repository page of the Bazel plugin]
to see what version of the IntelliJ IDEA it is actually compatible with.
Also note that the version of the Bazel plugin used in turn may or may not be
compatible with the Bazel version used.
In addition, Java 8 must be specified on your path or via `JAVA_HOME` so that
building with Bazel via the Bazel plugin is possible.
@ -13,46 +27,64 @@ TIP: If the synchronization of the project with the BUILD files using the Bazel
plugin fails and IntelliJ reports the error **Could not get Bazel roots**, this
indicates that the Bazel plugin couldn't find Java 8.
Bazel must be installed as described by
<<dev-bazel#installation,Building with Bazel - Installation>>.
=== Installation of IntelliJ IDEA
Please refer to the
link:https://www.jetbrains.com/help/idea/installation-guide.html[installation guide provided by Jetbrains]
to install it on your platform. Make sure to install a version compatible with
the Bazel plugin as mentioned above.
== Installation of the Bazel plugin
The plugin is usually installed using the Jetbrains plugin repository as shown
in the steps below, but it's also possible to
link:https://github.com/bazelbuild/intellij[build it from source].
. Go to *File -> Settings -> Plugins*.
. Click on *Browse Repositories*.
. Search for the plugin `IntelliJ with Bazel`.
+
(Or, from the welcome screen, *Configure -> Plugins*)
. Activate the *Marketplace* tab.
. Search for the plugin `Bazel` (by Google).
+
TIP: In case the Bazel plugin is not listed, or if it shows an outdated version,
verify the compatibility between the Bazel plugin and IntelliJ IDEA on link:https://plugins.jetbrains.com/plugin/8609-bazel/versions[the JetBrains plugin page].
. Install it.
. Restart IntelliJ.
. Restart IntelliJ IDEA.
TIP: If your project's Bazel build fails with **Cannot run program "bazel": No
such file or directory**, then you may have to set the binary location in the
Bazel plugin settings:
[TIP]
====
If your project's Bazel build fails with **Cannot run program "bazel": No such
file or directory**, then you may have to set the binary location in the Bazel
plugin settings:
. Go to Preferences -> Other Settings -> Bazel Settings.
. Set the Bazel binary location.
. Go to *Preferences -> Other Settings -> Bazel Settings*.
. Set the *Bazel binary location*.
====
== Creation of IntelliJ project
== Creation of the project
. Go to *File -> Import Bazel Project*.
+
(Or, from the welcome screen, *Import Bazel Project* should already be shown in
there.)
. For *Use existing bazel workspace -> Workspace*, select the directory
containing the Gerrit source code.
. Choose *Import from workspace* and select the `.bazelproject` file which is
located in the top directory of the Gerrit source code.
. Adjust the path of the project data directory and the name of the project if
desired.
. Finish the creation of the project.
. Verify that you can now build the project. Hit the button with the Bazel icon
(located on the top-right by default) to synchronize the project. Note that
warnings may be present in the build.
At this point all the basic functionality should be working such as Java class
inspection and running <<unit-tests,unit tests>>.
TIP: The project data directory can be separate from the source code. One
advantage of this is that project files don't need to be excluded from version
control.
Unfortunately, the created project seems to have a broken output path. To fix
it, please complete the following steps:
. Go to *File -> Project Structure -> Project Settings -> Modules*.
. Switch to the tab *Paths*.
. Click on *Inherit project compile output path*.
. Click on *Use module compile output path*.
== Recommended settings
=== Code style
@ -61,17 +93,20 @@ it, please complete the following steps:
Install the `google-java-format` plugin by following these steps:
. Go to *File -> Settings -> Plugins*.
. Click on *Browse Repositories*.
. Search for the plugin `google-java-format`.
. Activate the *Marketplace* tab.
. Search for the plugin `google-java-format` by Google.
. Install it.
. Restart IntelliJ.
. Restart IntelliJ IDEA.
Every time you start IntelliJ, make sure to use *Code -> Reformat with
Every time you start IntelliJ IDEA, make sure to use *Code -> Reformat with
google-java-format* on an arbitrary line of code. This replaces the default
CodeStyleManager with a custom one. Thus, uses of *Reformat Code* either via
*Code -> Reformat Code*, keyboard shortcuts, or the commit dialog will use the
custom style defined by the `google-java-format` plugin.
Please refer to the documentation on the <<dev-contributing#style,code style>>
for which version of `google-java-format` is used with Gerrit.
==== Code style settings
The `google-java-format` plugin is the preferred way to format the code. As it
only kicks in on demand, it's also recommended to have code style settings
@ -84,11 +119,10 @@ to be as close as possible. So before submitting code, please make sure to run
https://raw.githubusercontent.com/google/styleguide/gh-pages/intellij-java-google-style.xml[
intellij-java-google-style.xml].
. Go to *File -> Settings -> Editor -> Code Style*.
. Click on *Manage*.
. Click on *Import*.
. Choose `IntelliJ IDEA Code Style XML`.
. Click on the wrench icon with the tooltip _Show Scheme Actions_.
. Click on *Import Scheme*.
. Select the previously downloaded file `intellij-java-google-style.xml`.
. Make sure that `Google Style` is chosen as *Scheme*.
. Make sure that `GoogleStyle` is chosen as the current *Scheme*.
In addition, the EditorConfig settings (which ensure a consistent style between
Eclipse, IntelliJ, and other editors) should be applied on top of that. Those
@ -97,7 +131,7 @@ will automatically pick up those settings if the EditorConfig plugin is enabled
and configured correctly as can be verified by:
. Go to *File -> Settings -> Plugins*.
. Ensure that the EditorConfig plugin is enabled.
. Ensure that the *EditorConfig* plugin (by JetBrains) is enabled.
. Go to *File -> Settings -> Editor -> Code Style*.
. Ensure that *Enable EditorConfig support* is checked.
@ -105,35 +139,42 @@ NOTE: If IntelliJ notifies you later on that the EditorConfig settings override
the code style settings, simply confirm that.
=== Copyright
Copy the folder `$(gerrit_source_code)/tools/intellij/copyright` (not just the
. Copy the folder `$(gerrit_source_code)/tools/intellij/copyright` (not just the
contents) to `$(project_data_directory)/.idea`. If it already exists, replace
it. Then go to *File -> Settings -> Editor -> Copyright -> Copyright Profiles*,
and import `Gerrit_Copyright.xml` to IntelliJ in case it doesn't pick the
copyright up automatically.
it. If you didn't select a custom data directory the command could look like
this, as run from the Gerrit source tree checkout as working directory:
+
----
cp -r tools/intellij/copyright .ijwb/.idea/
----
. Go to *File -> Settings -> Editor -> Copyright -> Copyright Profiles*.
. Verify that the *Gerrit Copyright* is now present there.
+
Only in case it hasn't picked up the copyright profile automatically, import
the `Gerrit_Copyright.xml` from that folder manually.
=== File header
By default, IntelliJ adds a file header containing the name of the author and
the current date to new files. To disable that, follow these steps:
=== Git integration
This section is only relevant in case you want to use the Git integration
plugin in IntelliJ IDEA.
. Go to *File -> Settings -> Editor -> File and Code Templates*.
. Select the tab *Includes*.
. Select *File Header*.
. Remove the template code in the right editor.
=== Commit message
To simplify the creation of commit messages which are compliant with the
<<dev-contributing#commit-message,Commit Message>> format, do the following:
. Go to *File -> Settings -> Version Control*.
. Check *Commit message right margin (columns)*.
. Make sure that 72 is specified as value.
. Check *Wrap when typing reaches right margin*.
. Go to *File -> Settings -> Version Control -> Commit Dialog*.
. In the *Commit message inspections*, activate the three inspections:
* *Blank line between subject and body*,
* *Limit body line* and
* *Limit subject line*.
. For the limit line inspections, make sure that 72 is specified as value.
. For *Limit body line*, tick *Show right margin* and *Wrap when typing reaches
right margin*.
In addition, you should follow the instructions of
<<dev-contributing#git_commit_settings,this section>> (if you haven't
done so already):
* Install the Git hook for the `Change-Id` line.
* Install the Git commit message hook for the `Change-Id` line.
* Set up the HTTP access.
Setting up the HTTP access will allow you to commit changes via IntelliJ without
@ -145,29 +186,18 @@ Run configurations can be accessed on the toolbar. To edit them or add new ones,
choose *Edit Configurations* on the drop-down list of the run configurations
or go to *Run -> Edit Configurations*.
=== Pre-configured run configurations
In order to be able to use the pre-configured run configurations, the following
steps are necessary:
. Make sure that the folder `runConfigurations` exists within
`$(project_data_directory)/.idea`. If it doesn't exist, create it.
. Specify the IntelliJ path variable `GERRIT_TESTSITE`. (This configuration is
shared among all IntelliJ projects.)
.. Go to *Settings -> Appearance & Behavior -> Path Variables*.
.. Click on the *+* to add a new path variable.
.. Specify `GERRIT_TESTSITE` as name and the path to your local test site as
value.
The copied run configurations will be added automatically to the available run
configurations of the IntelliJ project.
[[runconfigurations-daemon]]
==== Gerrit Daemon
WARNING: At the moment running this configuration results in a
[WARNING]
====
At the moment running this (local) configuration results in a
`java.io.FileNotFoundException`. To debug a local Gerrit server with IntelliJ,
use the instructions of <<dev-readme#run_daemon,Running the Daemon>> in
combination with <<remote-debug,Debugging a remote Gerrit server>>.
(link:https://bugs.chromium.org/p/gerrit/issues/detail?id=11360[Issue 11360])
====
Copy `$(gerrit_source_code)/tools/intellij/gerrit_daemon.xml` to
`$(project_data_directory)/.idea/runConfigurations/`.
@ -177,10 +207,12 @@ This run configuration starts the Gerrit daemon similarly as
NOTE: The <<dev-readme#init,Site Initialization>> has to be completed
before this run configuration works properly.
[[unit-tests]]
=== Unit tests
To create run configurations for unit tests, run or debug them via a right-click
on a method, class, file, or package. The created run configuration is a
temporary one and can be saved to make it permanent.
temporary one and can be saved to make it permanent by selecting *Create
'Bazel test [...]'...* from the context menu.
Normally, this approach generates JUnit run configurations. When the Bazel
plugin manages a project, it intercepts the creation and creates a Bazel test
@ -193,10 +225,15 @@ IntelliJ via a `Remote debug configuration`.
. Go to *Run -> Edit Configurations*.
. Click on the *+* to add a new configuration.
. Choose *Remote*.
. Choose *Remote* from the *Templates*.
. Adjust *Configuration -> Settings -> Host* and *Port*.
. Start this configuration in `Debug` mode.
TIP: This run configuration dialog also shows the line for the JVM as startup
flag that you can copy to include in your
`$(gerrit_test_site)/etc/gerrit.config` in the `[container]` section in order
to work-around the <<runconfigurations-daemon,local run configuration issue>>.
GERRIT
------
Part of link:index.html[Gerrit Code Review]

View File

@ -251,7 +251,10 @@ link:https://gerrit-review.googlesource.com/admin/repos/gerrit,branches[
Gerrit Web UI] or by push.
* Push the commits done on `stable-$version` to `refs/for/stable-$version` and
get them merged
get them merged.
* Create a change updating the `defaultbranch` field in the `.gitreview`
to match the branch name created.
[[push-tag]]

View File

@ -4934,12 +4934,22 @@ need the FileInfo should make two requests.
The request parameter `q` changes the response to return a list
of all files (modified or unmodified) that contain that substring
in the path name. This is useful to implement suggestion services
finding a file by partial name.
finding a file by partial name. Clients that also need the FileInfo
should make two requests.
The integer-valued request parameter `parent` changes the response to return a
list of the files which are different in this commit compared to the given
parent commit. This is useful for supporting review of merge commits. The value
is the 1-based index of the parent's position in the commit object.
For merge commits only, the integer-valued request parameter `parent`
changes the response to return a map of the files which are different
in this commit compared to the given parent commit. The value is the
1-based index of the parent's position in the commit object. If not
specified, the response contains a map of the files different in the
auto merge result.
The request parameter `base` changes the response to return a map of the
files which are different in this commit compared to the given revision. The
revision must correspond to a patch set in the change.
The `reviewed`, `q`, `parent`, and `base` options are mutually exclusive.
That is, only one of them may be used at a time.
.Request
----

View File

@ -1062,8 +1062,8 @@ maven_jar(
# and httpasyncclient as necessary.
maven_jar(
name = "elasticsearch-rest-client",
artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.2.0",
sha1 = "39cf34068b0af284eaa9b8bd86a131cb24b322d5",
artifact = "org.elasticsearch.client:elasticsearch-rest-client:7.3.1",
sha1 = "f5793c89b50a159cbb3e15e17bb981ff854cbe51",
)
maven_jar(

View File

@ -32,6 +32,7 @@ public @interface UsedAt {
/** Enumeration of projects that call a method/type/field. */
enum Project {
GOOGLE,
COLLABNET,
PLUGIN_CHECKS,
PLUGIN_DELETE_PROJECT,
PLUGIN_SERVICEUSER,

View File

@ -25,9 +25,11 @@ public enum ElasticVersion {
V6_5("6.5.*"),
V6_6("6.6.*"),
V6_7("6.7.*"),
V6_8("6.8.*"),
V7_0("7.0.*"),
V7_1("7.1.*"),
V7_2("7.2.*");
V7_2("7.2.*"),
V7_3("7.3.*");
private final String version;
private final Pattern pattern;

View File

@ -335,6 +335,15 @@ public interface ChangeApi {
*/
Map<String, List<CommentInfo>> comments() throws RestApiException;
/**
* Get all published comments on a change as a list.
*
* @return comments as a list; comments have the {@code revision} field set to indicate their
* patch set.
* @throws RestApiException
*/
List<CommentInfo> commentsAsList() throws RestApiException;
/**
* Get all robot comments on a change.
*
@ -353,6 +362,15 @@ public interface ChangeApi {
*/
Map<String, List<CommentInfo>> drafts() throws RestApiException;
/**
* Get all draft comments for the current user on a change as a list.
*
* @return drafts as a list; comments have the {@code revision} field set to indicate their patch
* set.
* @throws RestApiException
*/
List<CommentInfo> draftsAsList() throws RestApiException;
ChangeInfo check() throws RestApiException;
ChangeInfo check(FixInput fix) throws RestApiException;
@ -580,6 +598,11 @@ public interface ChangeApi {
throw new NotImplementedException();
}
@Override
public List<CommentInfo> commentsAsList() throws RestApiException {
throw new NotImplementedException();
}
@Override
public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
throw new NotImplementedException();
@ -590,6 +613,11 @@ public interface ChangeApi {
throw new NotImplementedException();
}
@Override
public List<CommentInfo> draftsAsList() throws RestApiException {
throw new NotImplementedException();
}
@Override
public ChangeInfo check() throws RestApiException {
throw new NotImplementedException();

View File

@ -17,10 +17,12 @@ package com.google.gerrit.extensions.api.projects;
import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.restapi.NotImplementedException;
import com.google.gerrit.extensions.restapi.RestApiException;
public interface CommitApi {
CommitInfo get() throws RestApiException;
ChangeApi cherryPick(CherryPickInput input) throws RestApiException;
@ -28,6 +30,11 @@ public interface CommitApi {
/** A default implementation for source compatibility when adding new methods to the interface. */
class NotImplemented implements CommitApi {
@Override
public CommitInfo get() throws RestApiException {
throw new NotImplementedException();
}
@Override
public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
throw new NotImplementedException();

View File

@ -66,7 +66,7 @@ public final class GerritLauncher {
}
/**
* Invokes a proram.
* Invokes a program.
*
* <p>Creates a new classloader to load and run the program class. To reuse a classloader across
* calls (e.g. from tests), use {@link #invokeProgram(ClassLoader, String[])}.
@ -176,7 +176,7 @@ public final class GerritLauncher {
}
/**
* Invokes a proram in the provided {@code ClassLoader}.
* Invokes a program in the provided {@code ClassLoader}.
*
* @param loader classloader to load program class from.
* @param origArgv arguments, as would be passed to {@code gerrit.war}. The first argument is the

View File

@ -558,6 +558,15 @@ class ChangeApiImpl implements ChangeApi {
}
}
@Override
public List<CommentInfo> commentsAsList() throws RestApiException {
try {
return listComments.getComments(change);
} catch (Exception e) {
throw asRestApiException("Cannot get comments", e);
}
}
@Override
public Map<String, List<RobotCommentInfo>> robotComments() throws RestApiException {
try {
@ -576,6 +585,15 @@ class ChangeApiImpl implements ChangeApi {
}
}
@Override
public List<CommentInfo> draftsAsList() throws RestApiException {
try {
return listDrafts.getComments(change);
} catch (Exception e) {
throw asRestApiException("Cannot get drafts", e);
}
}
@Override
public ChangeInfo check() throws RestApiException {
try {

View File

@ -21,10 +21,12 @@ import com.google.gerrit.extensions.api.changes.Changes;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.api.projects.CommitApi;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.project.CommitResource;
import com.google.gerrit.server.restapi.change.CherryPickCommit;
import com.google.gerrit.server.restapi.project.CommitIncludedIn;
import com.google.gerrit.server.restapi.project.GetCommit;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@ -34,6 +36,7 @@ public class CommitApiImpl implements CommitApi {
}
private final Changes changes;
private final GetCommit getCommit;
private final CherryPickCommit cherryPickCommit;
private final CommitIncludedIn includedIn;
private final CommitResource commitResource;
@ -41,15 +44,26 @@ public class CommitApiImpl implements CommitApi {
@Inject
CommitApiImpl(
Changes changes,
GetCommit getCommit,
CherryPickCommit cherryPickCommit,
CommitIncludedIn includedIn,
@Assisted CommitResource commitResource) {
this.changes = changes;
this.getCommit = getCommit;
this.cherryPickCommit = cherryPickCommit;
this.includedIn = includedIn;
this.commitResource = commitResource;
}
@Override
public CommitInfo get() throws RestApiException {
try {
return getCommit.apply(commitResource).value();
} catch (Exception e) {
throw asRestApiException("Cannot get commit info", e);
}
}
@Override
public ChangeApi cherryPick(CherryPickInput input) throws RestApiException {
try {

View File

@ -31,7 +31,7 @@ public abstract class AuthRequest {
/**
* Returns the username to be authenticated.
*
* @return username for authentication or null for anonymous access.
* @return username for authentication or {@code empty} for anonymous access.
*/
public final Optional<String> getUsername() {
return username;
@ -40,7 +40,7 @@ public abstract class AuthRequest {
/**
* Returns the user's credentials
*
* @return user's credentials or null
* @return user's credentials or {@code empty}.
*/
public final Optional<String> getPassword() {
return password;

View File

@ -19,6 +19,8 @@ import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.AccountState;
import com.google.gerrit.server.config.ChangeCleanupConfig;
import com.google.gerrit.server.plugincontext.PluginItemContext;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
@ -30,10 +32,17 @@ import java.util.Collection;
@Singleton
public class BatchAbandon {
private final AbandonOp.Factory abandonOpFactory;
private final ChangeCleanupConfig cfg;
private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
@Inject
BatchAbandon(AbandonOp.Factory abandonOpFactory) {
BatchAbandon(
AbandonOp.Factory abandonOpFactory,
ChangeCleanupConfig cfg,
PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore) {
this.abandonOpFactory = abandonOpFactory;
this.cfg = cfg;
this.accountPatchReviewStore = accountPatchReviewStore;
}
/**
@ -67,6 +76,10 @@ public class BatchAbandon {
u.addOp(change.getId(), abandonOpFactory.create(accountState, msgTxt));
}
u.execute();
if (cfg.getCleanupAccountPatchReview()) {
cleanupAccountPatchReview(changes);
}
}
}
@ -88,4 +101,10 @@ public class BatchAbandon {
throws RestApiException, UpdateException {
batchAbandon(updateFactory, project, user, changes, "", NotifyResolver.Result.all());
}
private void cleanupAccountPatchReview(Collection<ChangeData> changes) {
for (ChangeData change : changes) {
accountPatchReviewStore.run(s -> s.clearReviewed(change.getId()));
}
}
}

View File

@ -29,6 +29,7 @@ public class ChangeCleanupConfig {
private static String KEY_ABANDON_AFTER = "abandonAfter";
private static String KEY_ABANDON_IF_MERGEABLE = "abandonIfMergeable";
private static String KEY_ABANDON_MESSAGE = "abandonMessage";
private static String KEY_CLEANUP_ACCOUNT_PATCH_REVIEW = "cleanupAccountPatchReview";
private static String DEFAULT_ABANDON_MESSAGE =
"Auto-Abandoned due to inactivity, see "
+ "${URL}\n"
@ -39,6 +40,7 @@ public class ChangeCleanupConfig {
private final Optional<Schedule> schedule;
private final long abandonAfter;
private final boolean abandonIfMergeable;
private final boolean cleanupAccountPatchReview;
private final String abandonMessage;
@Inject
@ -47,6 +49,8 @@ public class ChangeCleanupConfig {
schedule = ScheduleConfig.createSchedule(cfg, SECTION);
abandonAfter = readAbandonAfter(cfg);
abandonIfMergeable = cfg.getBoolean(SECTION, null, KEY_ABANDON_IF_MERGEABLE, true);
cleanupAccountPatchReview =
cfg.getBoolean(SECTION, null, KEY_CLEANUP_ACCOUNT_PATCH_REVIEW, false);
abandonMessage = readAbandonMessage(cfg);
}
@ -73,6 +77,10 @@ public class ChangeCleanupConfig {
return abandonIfMergeable;
}
public boolean getCleanupAccountPatchReview() {
return cleanupAccountPatchReview;
}
public String getAbandonMessage() {
String docUrl =
urlFormatter.get().getDocUrl("user-change-cleanup.html", "auto-abandon").orElse("");

View File

@ -29,10 +29,10 @@ public class DefaultChangeReportFormatter implements ChangeReportFormatter {
private static final int SUBJECT_CROP_RANGE = 10;
private static final String NEW_CHANGE_INDICATOR = " [NEW]";
private final DynamicItem<UrlFormatter> urlFormatter;
protected final DynamicItem<UrlFormatter> urlFormatter;
@Inject
DefaultChangeReportFormatter(DynamicItem<UrlFormatter> urlFormatter) {
public DefaultChangeReportFormatter(DynamicItem<UrlFormatter> urlFormatter) {
this.urlFormatter = urlFormatter;
}

View File

@ -17,10 +17,13 @@ package com.google.gerrit.server.query.account;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.UsedAt;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.IndexConfig;
import com.google.gerrit.index.Schema;
@ -41,6 +44,8 @@ import java.util.Set;
* holding on to a single instance.
*/
public class InternalAccountQuery extends InternalQuery<AccountState, InternalAccountQuery> {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@Inject
InternalAccountQuery(
AccountQueryProcessor queryProcessor,
@ -61,6 +66,23 @@ public class InternalAccountQuery extends InternalQuery<AccountState, InternalAc
return query(AccountPredicates.externalIdIncludingSecondaryEmails(externalId.toString()));
}
@UsedAt(UsedAt.Project.COLLABNET)
public AccountState oneByExternalId(ExternalId.Key externalId) {
List<AccountState> accountStates = byExternalId(externalId);
if (accountStates.size() == 1) {
return accountStates.get(0);
} else if (accountStates.size() > 0) {
StringBuilder msg = new StringBuilder();
msg.append("Ambiguous external ID ").append(externalId).append(" for accounts: ");
Joiner.on(", ")
.appendTo(
msg,
accountStates.stream().map(a -> a.getAccount().id().toString()).collect(toList()));
logger.atWarning().log(msg.toString());
}
return null;
}
public List<AccountState> byFullName(String fullName) {
return query(AccountPredicates.fullName(fullName));
}

View File

@ -19,6 +19,7 @@ import com.google.common.flogger.FluentLogger;
import com.google.common.hash.Hasher;
import com.google.common.hash.Hashing;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.extensions.api.GerritApi;
import com.google.gerrit.extensions.common.FileInfo;
import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.restapi.AuthException;
@ -27,8 +28,8 @@ import com.google.gerrit.extensions.restapi.CacheControl;
import com.google.gerrit.extensions.restapi.ChildCollection;
import com.google.gerrit.extensions.restapi.ETagView;
import com.google.gerrit.extensions.restapi.IdString;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestView;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
@ -118,6 +119,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
private final PatchListCache patchListCache;
private final PatchSetUtil psUtil;
private final PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore;
private final GerritApi gApi;
@Inject
ListFiles(
@ -127,7 +129,8 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
GitRepositoryManager gitManager,
PatchListCache patchListCache,
PatchSetUtil psUtil,
PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore) {
PluginItemContext<AccountPatchReviewStore> accountPatchReviewStore,
GerritApi gApi) {
this.self = self;
this.fileInfoJson = fileInfoJson;
this.revisions = revisions;
@ -135,6 +138,7 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
this.patchListCache = patchListCache;
this.psUtil = psUtil;
this.accountPatchReviewStore = accountPatchReviewStore;
this.gApi = gApi;
}
public ListFiles setReviewed(boolean r) {
@ -144,9 +148,8 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
@Override
public Response<?> apply(RevisionResource resource)
throws AuthException, BadRequestException, ResourceNotFoundException,
RepositoryNotFoundException, IOException, PatchListNotAvailableException,
PermissionBackendException {
throws RestApiException, RepositoryNotFoundException, IOException,
PatchListNotAvailableException, PermissionBackendException {
checkOptions();
if (reviewed) {
return Response.ok(reviewed(resource));
@ -164,7 +167,17 @@ public class Files implements ChildCollection<RevisionResource, FileResource> {
resource.getChange(),
resource.getPatchSet().commitId(),
baseResource.getPatchSet()));
} else if (parentNum > 0) {
} else if (parentNum != 0) {
int parents =
gApi.changes()
.id(resource.getChange().getChangeId())
.revision(resource.getPatchSet().id().get())
.commit(false)
.parents
.size();
if (parentNum < 0 || parentNum > parents) {
throw new BadRequestException(String.format("invalid parent number: %d", parentNum));
}
r =
Response.ok(
fileInfoJson.toFileInfoMap(

View File

@ -14,46 +14,37 @@
package com.google.gerrit.server.restapi.change;
import com.google.gerrit.extensions.common.CommentInfo;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.Response;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Comment;
import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.List;
import java.util.Map;
@Singleton
public class ListChangeComments implements RestReadView<ChangeResource> {
private final ChangeData.Factory changeDataFactory;
private final Provider<CommentJson> commentJson;
private final CommentsUtil commentsUtil;
public class ListChangeComments extends ListChangeDrafts {
@Inject
ListChangeComments(
ChangeData.Factory changeDataFactory,
Provider<CommentJson> commentJson,
CommentsUtil commentsUtil) {
this.changeDataFactory = changeDataFactory;
this.commentJson = commentJson;
this.commentsUtil = commentsUtil;
super(changeDataFactory, commentJson, commentsUtil);
}
@Override
public Response<Map<String, List<CommentInfo>>> apply(ChangeResource rsrc)
throws AuthException, PermissionBackendException {
protected Iterable<Comment> listComments(ChangeResource rsrc) {
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
return Response.ok(
commentJson
.get()
.setFillAccounts(true)
.setFillPatchSet(true)
.newCommentFormatter()
.format(commentsUtil.publishedByChange(cd.notes())));
return commentsUtil.publishedByChange(cd.notes());
}
@Override
protected boolean includeAuthorInfo() {
return true;
}
@Override
public boolean requireAuthentication() {
return false;
}
}

View File

@ -23,6 +23,7 @@ import com.google.gerrit.server.CommentsUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.restapi.change.CommentJson.CommentFormatter;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@ -31,9 +32,9 @@ import java.util.Map;
@Singleton
public class ListChangeDrafts implements RestReadView<ChangeResource> {
private final ChangeData.Factory changeDataFactory;
private final Provider<CommentJson> commentJson;
private final CommentsUtil commentsUtil;
protected final ChangeData.Factory changeDataFactory;
protected final Provider<CommentJson> commentJson;
protected final CommentsUtil commentsUtil;
@Inject
ListChangeDrafts(
@ -45,21 +46,41 @@ public class ListChangeDrafts implements RestReadView<ChangeResource> {
this.commentsUtil = commentsUtil;
}
protected Iterable<Comment> listComments(ChangeResource rsrc) {
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
return commentsUtil.draftByChangeAuthor(cd.notes(), rsrc.getUser().getAccountId());
}
protected boolean includeAuthorInfo() {
return false;
}
public boolean requireAuthentication() {
return true;
}
@Override
public Response<Map<String, List<CommentInfo>>> apply(ChangeResource rsrc)
throws AuthException, PermissionBackendException {
if (!rsrc.getUser().isIdentifiedUser()) {
if (requireAuthentication() && !rsrc.getUser().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
ChangeData cd = changeDataFactory.create(rsrc.getNotes());
List<Comment> drafts =
commentsUtil.draftByChangeAuthor(cd.notes(), rsrc.getUser().getAccountId());
return Response.ok(
commentJson
.get()
.setFillAccounts(false)
.setFillPatchSet(true)
.newCommentFormatter()
.format(drafts));
return Response.ok(getCommentFormatter().format(listComments(rsrc)));
}
public List<CommentInfo> getComments(ChangeResource rsrc)
throws AuthException, PermissionBackendException {
if (requireAuthentication() && !rsrc.getUser().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
return getCommentFormatter().formatAsList(listComments(rsrc));
}
private CommentFormatter getCommentFormatter() {
return commentJson
.get()
.setFillAccounts(includeAuthorInfo())
.setFillPatchSet(true)
.newCommentFormatter();
}
}

View File

@ -84,7 +84,6 @@ public class PrologEnvironment extends BufferingPrologControl {
public void setPredicate(Predicate goal) {
super.setPredicate(goal);
int reductionLimit = args.reductionLimit(goal);
logger.atFine().log("setting reductionLimit %d", reductionLimit);
setReductionLimit(reductionLimit);
}
@ -223,6 +222,9 @@ public class PrologEnvironment extends BufferingPrologControl {
private int reductionLimit(Predicate goal) {
if (goal.getClass() == CONSULT_STREAM_2) {
logger.atFine().log(
"predicate class is CONSULT_STREAM_2: override reductionLimit with compileLimit (%d)",
compileLimit);
return compileLimit;
}
return reductionLimit;

View File

@ -320,6 +320,21 @@ public class AccountIT extends AbstractDaemonTest {
RefUpdateCounter.projectRef(allUsers, RefNames.REFS_SEQUENCES + Sequences.NAME_ACCOUNTS));
}
@Test
public void createWithInvalidEmailAddress() throws Exception {
AccountInput input = new AccountInput();
input.username = name("test");
input.email = "invalid email address";
// Invalid email address should cause the creation to fail
BadRequestException thrown =
assertThrows(BadRequestException.class, () -> gApi.accounts().create(input));
assertThat(thrown).hasMessageThat().isEqualTo("invalid email address");
// The account should not have been created
assertThrows(ResourceNotFoundException.class, () -> gApi.accounts().id(input.username).get());
}
private Account.Id createByAccountCreator(int expectedAccountReindexCalls) throws Exception {
String name = "foo";
TestAccount foo = accountCreator.create(name);

View File

@ -90,10 +90,12 @@ import com.google.gerrit.common.data.LabelType;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.annotations.Exports;
import com.google.gerrit.extensions.api.accounts.DeleteDraftCommentsInput;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.AddReviewerResult;
import com.google.gerrit.extensions.api.changes.DeleteReviewerInput;
import com.google.gerrit.extensions.api.changes.DeleteVoteInput;
import com.google.gerrit.extensions.api.changes.DraftApi;
import com.google.gerrit.extensions.api.changes.DraftInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.NotifyInfo;
@ -4189,7 +4191,7 @@ public class ChangeIT extends AbstractDaemonTest {
submittableAfterLosingPermissions("Label");
}
public void submittableAfterLosingPermissions(String label) throws Exception {
private void submittableAfterLosingPermissions(String label) throws Exception {
String codeReviewLabel = "Code-Review";
AccountGroup.UUID registered = REGISTERED_USERS;
projectOperations
@ -4242,6 +4244,46 @@ public class ChangeIT extends AbstractDaemonTest {
gApi.changes().id(changeId).current().submit();
}
@Test
public void draftCommentsShouldNotUpdateChangeTimestamp() throws Exception {
String changeId = createNewChange();
Timestamp changeTs = getChangeLastUpdate(changeId);
DraftApi draftApi = addDraftComment(changeId);
assertThat(getChangeLastUpdate(changeId)).isEqualTo(changeTs);
draftApi.delete();
assertThat(getChangeLastUpdate(changeId)).isEqualTo(changeTs);
}
@Test
public void deletingAllDraftCommentsShouldNotUpdateChangeTimestamp() throws Exception {
String changeId = createNewChange();
Timestamp changeTs = getChangeLastUpdate(changeId);
addDraftComment(changeId);
assertThat(getChangeLastUpdate(changeId)).isEqualTo(changeTs);
gApi.accounts().self().deleteDraftComments(new DeleteDraftCommentsInput());
assertThat(getChangeLastUpdate(changeId)).isEqualTo(changeTs);
}
private Timestamp getChangeLastUpdate(String changeId) throws RestApiException {
Timestamp changeTs = gApi.changes().id(changeId).get().updated;
return changeTs;
}
private String createNewChange() throws Exception {
TestRepository<InMemoryRepository> userRepo = cloneProject(project, user);
PushOneCommit.Result result =
pushFactory.create(user.newIdent(), userRepo).to("refs/for/master");
String changeId = result.getChangeId();
return changeId;
}
private DraftApi addDraftComment(String changeId) throws RestApiException {
DraftInput comment = new DraftInput();
comment.message = "foo";
comment.path = "/foo";
return gApi.changes().id(changeId).current().createDraft(comment);
}
private String getCommitMessage(String changeId) throws RestApiException, IOException {
return gApi.changes().id(changeId).current().file("/COMMIT_MSG").content().asString();
}

View File

@ -0,0 +1,160 @@
// Copyright (C) 2017 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.
// 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.
package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static java.util.stream.Collectors.toList;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit.Result;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.api.projects.TagInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.GitPerson;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.reviewdb.client.BranchNameKey;
import com.google.inject.Inject;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Test;
@NoHttpd
public class CommitIT extends AbstractDaemonTest {
@Inject private ProjectOperations projectOperations;
@Test
public void getCommitInfo() throws Exception {
Result result = createChange();
String commitId = result.getCommit().getId().name();
CommitInfo info = gApi.projects().name(project.get()).commit(commitId).get();
assertThat(info.commit).isEqualTo(commitId);
assertThat(info.parents.stream().map(c -> c.commit).collect(toList()))
.containsExactly(result.getCommit().getParent(0).name());
assertThat(info.subject).isEqualTo(result.getCommit().getShortMessage());
assertPerson(info.author, admin);
assertPerson(info.committer, admin);
assertThat(info.webLinks).isNull();
}
@Test
public void includedInOpenChange() throws Exception {
Result result = createChange();
assertThat(getIncludedIn(result.getCommit().getId()).branches).isEmpty();
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
}
@Test
public void includedInMergedChange() throws Exception {
Result result = createChange();
gApi.changes()
.id(result.getChangeId())
.revision(result.getCommit().name())
.review(ReviewInput.approve());
gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
assertThat(getIncludedIn(result.getCommit().getId()).branches).containsExactly("master");
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.CREATE_TAG).ref(R_TAGS + "*").group(adminGroupUuid()))
.update();
gApi.projects().name(result.getChange().project().get()).tag("test-tag").create(new TagInput());
assertThat(getIncludedIn(result.getCommit().getId()).tags).containsExactly("test-tag");
createBranch(BranchNameKey.create(project, "test-branch"));
assertThat(getIncludedIn(result.getCommit().getId()).branches)
.containsExactly("master", "test-branch");
}
@Test
public void cherryPickCommitWithoutChangeId() throws Exception {
// This test is a little superfluous, since the current cherry-pick code ignores
// the commit message of the to-be-cherry-picked change, using the one in
// CherryPickInput instead.
CherryPickInput input = new CherryPickInput();
input.destination = "foo";
input.message = "it goes to foo branch";
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
RevCommit revCommit = createNewCommitWithoutChangeId("refs/heads/master", "a.txt", "content");
ChangeInfo changeInfo =
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
assertThat(changeInfo.messages).hasSize(1);
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
String expectedMessage =
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
assertThat(revInfo).isNotNull();
CommitInfo commitInfo = revInfo.commit;
assertThat(commitInfo.message)
.isEqualTo(input.message + "\n\nChange-Id: " + changeInfo.changeId + "\n");
}
@Test
public void cherryPickCommitWithChangeId() throws Exception {
CherryPickInput input = new CherryPickInput();
input.destination = "foo";
RevCommit revCommit = createChange().getCommit();
List<String> footers = revCommit.getFooterLines("Change-Id");
assertThat(footers).hasSize(1);
String changeId = footers.get(0);
input.message = "it goes to foo branch\n\nChange-Id: " + changeId;
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
ChangeInfo changeInfo =
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
assertThat(changeInfo.messages).hasSize(1);
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
String expectedMessage =
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
assertThat(revInfo).isNotNull();
assertThat(revInfo.commit.message).isEqualTo(input.message + "\n");
}
private IncludedInInfo getIncludedIn(ObjectId id) throws Exception {
return gApi.projects().name(project.get()).commit(id.name()).includedIn();
}
private static void assertPerson(GitPerson actual, TestAccount expected) {
assertThat(actual.email).isEqualTo(expected.email());
assertThat(actual.name).isEqualTo(expected.fullName());
}
}

View File

@ -1,75 +0,0 @@
// Copyright (C) 2017 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.
// 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.
package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.NoHttpd;
import com.google.gerrit.acceptance.PushOneCommit.Result;
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.TagInput;
import com.google.gerrit.reviewdb.client.BranchNameKey;
import com.google.inject.Inject;
import org.eclipse.jgit.lib.ObjectId;
import org.junit.Test;
@NoHttpd
public class CommitIncludedInIT extends AbstractDaemonTest {
@Inject private ProjectOperations projectOperations;
@Test
public void includedInOpenChange() throws Exception {
Result result = createChange();
assertThat(getIncludedIn(result.getCommit().getId()).branches).isEmpty();
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
}
@Test
public void includedInMergedChange() throws Exception {
Result result = createChange();
gApi.changes()
.id(result.getChangeId())
.revision(result.getCommit().name())
.review(ReviewInput.approve());
gApi.changes().id(result.getChangeId()).revision(result.getCommit().name()).submit();
assertThat(getIncludedIn(result.getCommit().getId()).branches).containsExactly("master");
assertThat(getIncludedIn(result.getCommit().getId()).tags).isEmpty();
projectOperations
.project(project)
.forUpdate()
.add(allow(Permission.CREATE_TAG).ref(R_TAGS + "*").group(adminGroupUuid()))
.update();
gApi.projects().name(result.getChange().project().get()).tag("test-tag").create(new TagInput());
assertThat(getIncludedIn(result.getCommit().getId()).tags).containsExactly("test-tag");
createBranch(BranchNameKey.create(project, "test-branch"));
assertThat(getIncludedIn(result.getCommit().getId()).branches)
.containsExactly("master", "test-branch");
}
private IncludedInInfo getIncludedIn(ObjectId id) throws Exception {
return gApi.projects().name(project.get()).commit(id.name()).includedIn();
}
}

View File

@ -1085,30 +1085,76 @@ public class RevisionIT extends AbstractDaemonTest {
PushOneCommit.Result r = createChange();
Map<String, FileInfo> files =
gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files();
assertThat(files).hasSize(2);
assertThat(Iterables.all(files.keySet(), f -> f.matches(FILE_NAME + '|' + COMMIT_MSG)))
.isTrue();
assertThat(files.keySet()).containsExactly(FILE_NAME, COMMIT_MSG);
}
@Test
public void filesOnMergeCommitChange() throws Exception {
PushOneCommit.Result r = createMergeCommitChange("refs/for/master");
// list files against auto-merge
// List files against auto-merge
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files().keySet())
.containsExactly(COMMIT_MSG, MERGE_LIST, "foo", "bar");
// list files against parent 1
// List files against parent 1
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(1).keySet())
.containsExactly(COMMIT_MSG, MERGE_LIST, "bar");
// list files against parent 2
// List files against parent 2
assertThat(gApi.changes().id(r.getChangeId()).revision(r.getCommit().name()).files(2).keySet())
.containsExactly(COMMIT_MSG, MERGE_LIST, "foo");
}
@Test
public void filesOnMergeCommitChangeWithInvalidParent() throws Exception {
PushOneCommit.Result r = createMergeCommitChange("refs/for/master");
BadRequestException thrown =
assertThrows(
BadRequestException.class,
() ->
gApi.changes()
.id(r.getChangeId())
.revision(r.getCommit().name())
.files(3)
.keySet());
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: 3");
thrown =
assertThrows(
BadRequestException.class,
() ->
gApi.changes()
.id(r.getChangeId())
.revision(r.getCommit().name())
.files(-1)
.keySet());
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: -1");
}
@Test
public void listFilesWithInvalidParent() throws Exception {
PushOneCommit.Result result1 = createChange();
String changeId = result1.getChangeId();
PushOneCommit.Result result2 = amendChange(changeId, SUBJECT, "b.txt", "b");
String revId2 = result2.getCommit().name();
BadRequestException thrown =
assertThrows(
BadRequestException.class,
() -> gApi.changes().id(changeId).revision(revId2).files(2).keySet());
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: 2");
thrown =
assertThrows(
BadRequestException.class,
() -> gApi.changes().id(changeId).revision(revId2).files(-1).keySet());
assertThat(thrown).hasMessageThat().isEqualTo("invalid parent number: -1");
}
@Test
public void listFilesOnDifferentBases() throws Exception {
RevCommit initialCommit = getHead(repo(), "HEAD");
PushOneCommit.Result result1 = createChange();
String changeId = result1.getChangeId();
PushOneCommit.Result result2 = amendChange(changeId, SUBJECT, "b.txt", "b");
@ -1131,6 +1177,19 @@ public class RevisionIT extends AbstractDaemonTest {
.containsExactly(COMMIT_MSG, "b.txt", "c.txt");
assertThat(gApi.changes().id(changeId).revision(revId3).files(revId2).keySet())
.containsExactly(COMMIT_MSG, "c.txt");
ResourceNotFoundException thrown =
assertThrows(
ResourceNotFoundException.class,
() -> gApi.changes().id(changeId).revision(revId3).files(initialCommit.getName()));
assertThat(thrown).hasMessageThat().contains(initialCommit.getName());
String invalidRev = "deadbeef";
thrown =
assertThrows(
ResourceNotFoundException.class,
() -> gApi.changes().id(changeId).revision(revId3).files(invalidRev));
assertThat(thrown).hasMessageThat().contains(invalidRev);
}
@Test

View File

@ -32,12 +32,12 @@ public class ElasticReindexIT extends AbstractReindexTests {
@ConfigSuite.Config
public static Config elasticsearchV6() {
return getConfig(ElasticVersion.V6_7);
return getConfig(ElasticVersion.V6_8);
}
@ConfigSuite.Config
public static Config elasticsearchV7() {
return getConfig(ElasticVersion.V7_2);
return getConfig(ElasticVersion.V7_3);
}
@Override

View File

@ -35,15 +35,11 @@ import com.google.gerrit.extensions.api.changes.ChangeApi;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.NotifyHandling;
import com.google.gerrit.extensions.api.changes.ReviewInput;
import com.google.gerrit.extensions.api.projects.BranchInput;
import com.google.gerrit.extensions.client.ChangeStatus;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeInput;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.CommitInfo;
import com.google.gerrit.extensions.common.MergeInput;
import com.google.gerrit.extensions.common.RevisionInfo;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@ -56,7 +52,6 @@ import com.google.gerrit.server.submit.ChangeAlreadyMergedException;
import com.google.gerrit.testing.FakeEmailSender.Message;
import com.google.gerrit.testing.TestTimeUtil;
import com.google.inject.Inject;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.jgit.lib.ObjectId;
@ -409,60 +404,6 @@ public class CreateChangeIT extends AbstractDaemonTest {
assertCreateSucceeds(in);
}
@Test
public void cherryPickCommitWithoutChangeId() throws Exception {
// This test is a little superfluous, since the current cherry-pick code ignores
// the commit message of the to-be-cherry-picked change, using the one in
// CherryPickInput instead.
CherryPickInput input = new CherryPickInput();
input.destination = "foo";
input.message = "it goes to foo branch";
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
RevCommit revCommit = createNewCommitWithoutChangeId("refs/heads/master", "a.txt", "content");
ChangeInfo changeInfo =
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
assertThat(changeInfo.messages).hasSize(1);
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
String expectedMessage =
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
assertThat(revInfo).isNotNull();
CommitInfo commitInfo = revInfo.commit;
assertThat(commitInfo.message)
.isEqualTo(input.message + "\n\nChange-Id: " + changeInfo.changeId + "\n");
}
@Test
public void cherryPickCommitWithChangeId() throws Exception {
CherryPickInput input = new CherryPickInput();
input.destination = "foo";
RevCommit revCommit = createChange().getCommit();
List<String> footers = revCommit.getFooterLines("Change-Id");
assertThat(footers).hasSize(1);
String changeId = footers.get(0);
input.message = "it goes to foo branch\n\nChange-Id: " + changeId;
gApi.projects().name(project.get()).branch(input.destination).create(new BranchInput());
ChangeInfo changeInfo =
gApi.projects().name(project.get()).commit(revCommit.getName()).cherryPick(input).get();
assertThat(changeInfo.messages).hasSize(1);
Iterator<ChangeMessageInfo> messageIterator = changeInfo.messages.iterator();
String expectedMessage =
String.format("Patch Set 1: Cherry Picked from commit %s.", revCommit.getName());
assertThat(messageIterator.next().message).isEqualTo(expectedMessage);
RevisionInfo revInfo = changeInfo.revisions.get(changeInfo.currentRevision);
assertThat(revInfo).isNotNull();
assertThat(revInfo.commit.message).isEqualTo(input.message + "\n");
}
@Test
public void createChangeOnExistingBranchNotPermitted() throws Exception {
createBranch(BranchNameKey.create(project, "foo"));

View File

@ -113,6 +113,11 @@ public class CommentsIT extends AbstractDaemonTest {
assertThat(result).hasSize(1);
CommentInfo actual = Iterables.getOnlyElement(result.get(comment.path));
assertThat(comment).isEqualTo(infoToDraft(path).apply(actual));
List<CommentInfo> list = getDraftCommentsAsList(changeId);
assertThat(list).hasSize(1);
actual = list.get(0);
assertThat(comment).isEqualTo(infoToDraft(path).apply(actual));
}
}
@ -134,6 +139,10 @@ public class CommentsIT extends AbstractDaemonTest {
Map<String, List<CommentInfo>> result = getDraftComments(changeId, revId);
assertThat(result).hasSize(1);
assertThat(result.get(path).stream().map(infoToDraft(path))).containsExactly(c1, c2, c3, c4);
List<CommentInfo> list = getDraftCommentsAsList(changeId);
assertThat(list).hasSize(4);
assertThat(list.stream().map(infoToDraft(path))).containsExactly(c1, c2, c3, c4);
}
}
@ -235,6 +244,9 @@ public class CommentsIT extends AbstractDaemonTest {
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
assertThat(result).isNotEmpty();
assertThat(result.get(file).stream().map(infoToInput(file))).containsExactly(c1, c2, c3, c4);
List<CommentInfo> list = getPublishedCommentsAsList(changeId);
assertThat(list.stream().map(infoToInput(file))).containsExactly(c1, c2, c3, c4);
}
// for the commit message comments on the auto-merge are not possible
@ -253,6 +265,9 @@ public class CommentsIT extends AbstractDaemonTest {
Map<String, List<CommentInfo>> result = getPublishedComments(changeId, revId);
assertThat(result).isNotEmpty();
assertThat(result.get(file).stream().map(infoToInput(file))).containsExactly(c1, c2, c3);
List<CommentInfo> list = getPublishedCommentsAsList(changeId);
assertThat(list.stream().map(infoToInput(file))).containsExactly(c1, c2, c3);
}
}
@ -308,6 +323,7 @@ public class CommentsIT extends AbstractDaemonTest {
String changeId = r.getChangeId();
String revId = r.getCommit().getName();
assertThat(getPublishedComments(changeId, revId)).isEmpty();
assertThat(getPublishedCommentsAsList(changeId)).isEmpty();
List<CommentInput> expectedComments = new ArrayList<>();
for (Integer line : lines) {
@ -324,6 +340,9 @@ public class CommentsIT extends AbstractDaemonTest {
List<CommentInfo> actualComments = result.get(file);
assertThat(actualComments.stream().map(infoToInput(file)))
.containsExactlyElementsIn(expectedComments);
List<CommentInfo> list = getPublishedCommentsAsList(changeId);
assertThat(list.stream().map(infoToInput(file))).containsExactlyElementsIn(expectedComments);
}
@Test
@ -1125,11 +1144,19 @@ public class CommentsIT extends AbstractDaemonTest {
return gApi.changes().id(changeId).revision(revId).comments();
}
private List<CommentInfo> getPublishedCommentsAsList(String changeId) throws Exception {
return gApi.changes().id(changeId).commentsAsList();
}
private Map<String, List<CommentInfo>> getDraftComments(String changeId, String revId)
throws Exception {
return gApi.changes().id(changeId).revision(revId).drafts();
}
private List<CommentInfo> getDraftCommentsAsList(String changeId) throws Exception {
return gApi.changes().id(changeId).draftsAsList();
}
private CommentInfo getDraftComment(String changeId, String revId, String uuid) throws Exception {
return gApi.changes().id(changeId).revision(revId).draft(uuid).get();
}

View File

@ -31,12 +31,12 @@ public class ElasticIndexIT extends AbstractIndexTests {
@ConfigSuite.Config
public static Config elasticsearchV6() {
return getConfig(ElasticVersion.V6_7);
return getConfig(ElasticVersion.V6_8);
}
@ConfigSuite.Config
public static Config elasticsearchV7() {
return getConfig(ElasticVersion.V7_2);
return getConfig(ElasticVersion.V7_3);
}
@Override

View File

@ -50,12 +50,16 @@ public class ElasticContainer extends ElasticsearchContainer {
return "blacktop/elasticsearch:6.6.2";
case V6_7:
return "blacktop/elasticsearch:6.7.2";
case V6_8:
return "blacktop/elasticsearch:6.8.2";
case V7_0:
return "blacktop/elasticsearch:7.0.1";
case V7_1:
return "blacktop/elasticsearch:7.1.1";
case V7_2:
return "blacktop/elasticsearch:7.2.0";
return "blacktop/elasticsearch:7.2.1";
case V7_3:
return "blacktop/elasticsearch:7.3.1";
}
throw new IllegalStateException("No tests for version: " + version.name());
}

View File

@ -41,7 +41,7 @@ public class ElasticV6QueryAccountsTest extends AbstractQueryAccountsTest {
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V6_7);
container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}

View File

@ -43,7 +43,7 @@ public class ElasticV6QueryChangesTest extends AbstractQueryChangesTest {
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V6_7);
container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}

View File

@ -41,7 +41,7 @@ public class ElasticV6QueryGroupsTest extends AbstractQueryGroupsTest {
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V6_7);
container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}

View File

@ -41,7 +41,7 @@ public class ElasticV6QueryProjectsTest extends AbstractQueryProjectsTest {
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V6_7);
container = ElasticContainer.createAndStart(ElasticVersion.V6_8);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}

View File

@ -41,7 +41,7 @@ public class ElasticV7QueryAccountsTest extends AbstractQueryAccountsTest {
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V7_2);
container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}

View File

@ -51,7 +51,7 @@ public class ElasticV7QueryChangesTest extends AbstractQueryChangesTest {
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V7_2);
container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
client = HttpAsyncClients.createDefault();
client.start();

View File

@ -41,7 +41,7 @@ public class ElasticV7QueryGroupsTest extends AbstractQueryGroupsTest {
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V7_2);
container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}

View File

@ -41,7 +41,7 @@ public class ElasticV7QueryProjectsTest extends AbstractQueryProjectsTest {
return;
}
container = ElasticContainer.createAndStart(ElasticVersion.V7_2);
container = ElasticContainer.createAndStart(ElasticVersion.V7_3);
nodeInfo = new ElasticNodeInfo(container.getHttpHost().getPort());
}

View File

@ -43,6 +43,9 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.forVersion("6.7.0")).isEqualTo(ElasticVersion.V6_7);
assertThat(ElasticVersion.forVersion("6.7.1")).isEqualTo(ElasticVersion.V6_7);
assertThat(ElasticVersion.forVersion("6.8.0")).isEqualTo(ElasticVersion.V6_8);
assertThat(ElasticVersion.forVersion("6.8.1")).isEqualTo(ElasticVersion.V6_8);
assertThat(ElasticVersion.forVersion("7.0.0")).isEqualTo(ElasticVersion.V7_0);
assertThat(ElasticVersion.forVersion("7.0.1")).isEqualTo(ElasticVersion.V7_0);
@ -51,6 +54,9 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.forVersion("7.2.0")).isEqualTo(ElasticVersion.V7_2);
assertThat(ElasticVersion.forVersion("7.2.1")).isEqualTo(ElasticVersion.V7_2);
assertThat(ElasticVersion.forVersion("7.3.0")).isEqualTo(ElasticVersion.V7_3);
assertThat(ElasticVersion.forVersion("7.3.1")).isEqualTo(ElasticVersion.V7_3);
}
@Test
@ -74,9 +80,11 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.V6_5.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V6_6.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V6_7.isAtLeastMinorVersion(ElasticVersion.V6_7)).isTrue();
assertThat(ElasticVersion.V6_8.isAtLeastMinorVersion(ElasticVersion.V6_8)).isTrue();
assertThat(ElasticVersion.V7_0.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V7_1.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V7_2.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
assertThat(ElasticVersion.V7_3.isAtLeastMinorVersion(ElasticVersion.V6_7)).isFalse();
}
@Test
@ -88,9 +96,11 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.V6_5.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_6.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_7.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V6_8.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_0.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_1.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_2.isV6OrLater()).isTrue();
assertThat(ElasticVersion.V7_3.isV6OrLater()).isTrue();
}
@Test
@ -102,8 +112,10 @@ public class ElasticVersionTest {
assertThat(ElasticVersion.V6_5.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V6_6.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V6_7.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V6_8.isV7OrLater()).isFalse();
assertThat(ElasticVersion.V7_0.isV7OrLater()).isTrue();
assertThat(ElasticVersion.V7_1.isV7OrLater()).isTrue();
assertThat(ElasticVersion.V7_2.isV7OrLater()).isTrue();
assertThat(ElasticVersion.V7_3.isV7OrLater()).isTrue();
}
}

File diff suppressed because one or more lines are too long

View File

@ -121,9 +121,11 @@ shortcuts are.
const Shortcut = {
OPEN_SHORTCUT_HELP_DIALOG: 'OPEN_SHORTCUT_HELP_DIALOG',
GO_TO_USER_DASHBOARD: 'GO_TO_USER_DASHBOARD',
GO_TO_OPENED_CHANGES: 'GO_TO_OPENED_CHANGES',
GO_TO_MERGED_CHANGES: 'GO_TO_MERGED_CHANGES',
GO_TO_ABANDONED_CHANGES: 'GO_TO_ABANDONED_CHANGES',
GO_TO_WATCHED_CHANGES: 'GO_TO_WATCHED_CHANGES',
CURSOR_NEXT_CHANGE: 'CURSOR_NEXT_CHANGE',
CURSOR_PREV_CHANGE: 'CURSOR_PREV_CHANGE',
@ -192,12 +194,16 @@ shortcuts are.
_describe(Shortcut.SEARCH, ShortcutSection.EVERYWHERE, 'Search');
_describe(Shortcut.OPEN_SHORTCUT_HELP_DIALOG, ShortcutSection.EVERYWHERE,
'Show this dialog');
_describe(Shortcut.GO_TO_USER_DASHBOARD, ShortcutSection.EVERYWHERE,
'Go to User Dashboard');
_describe(Shortcut.GO_TO_OPENED_CHANGES, ShortcutSection.EVERYWHERE,
'Go to Opened Changes');
_describe(Shortcut.GO_TO_MERGED_CHANGES, ShortcutSection.EVERYWHERE,
'Go to Merged Changes');
_describe(Shortcut.GO_TO_ABANDONED_CHANGES, ShortcutSection.EVERYWHERE,
'Go to Abandoned Changes');
_describe(Shortcut.GO_TO_WATCHED_CHANGES, ShortcutSection.EVERYWHERE,
'Go to Watched Changes');
_describe(Shortcut.CURSOR_NEXT_CHANGE, ShortcutSection.ACTIONS,
'Select next change');

View File

@ -204,10 +204,11 @@
});
for (const key of keys) {
if (!values[key]) { return; }
let text = values[key];
if (!text) { text = ''; }
// The value from the server being used to choose which item is
// selected is in integer form, so this must be converted.
valuesArr.push({value: parseInt(key, 10), text: values[key]});
valuesArr.push({value: parseInt(key, 10), text});
}
return valuesArr;
},

View File

@ -349,6 +349,13 @@ limitations under the License.
return this._navigate(this.getUrlForSearchQuery(query, opt_offset));
},
/**
* Navigate to the user's dashboard
*/
navigateToUserDashboard() {
return this._navigate(this.getUrlForUserDashboard('self'));
},
/**
* @param {!Object} change The change object.
* @param {number=} opt_patchNum

View File

@ -1424,9 +1424,13 @@
},
_handleSettingsLegacyRoute(data) {
// email tokens may contain '+' but no space.
// The parameter parsing replaces all '+' with a space,
// undo that to have valid tokens.
const token = data.params[0].replace(/ /g, '+');
this._setParams({
view: Gerrit.Nav.View.SETTINGS,
emailToken: data.params[0],
emailToken: token,
});
},

View File

@ -672,6 +672,14 @@ limitations under the License.
});
});
test('_handleSettingsLegacyRoute with +', () => {
const data = {params: {0: 'my-token test'}};
assertDataToParams(data, '_handleSettingsLegacyRoute', {
view: Gerrit.Nav.View.SETTINGS,
emailToken: 'my-token+test',
});
});
test('_handleSettingsRoute', () => {
const data = {};
assertDataToParams(data, '_handleSettingsRoute', {

View File

@ -106,9 +106,11 @@
keyboardShortcuts() {
return {
[this.Shortcut.OPEN_SHORTCUT_HELP_DIALOG]: '_showKeyboardShortcuts',
[this.Shortcut.GO_TO_USER_DASHBOARD]: '_goToUserDashboard',
[this.Shortcut.GO_TO_OPENED_CHANGES]: '_goToOpenedChanges',
[this.Shortcut.GO_TO_MERGED_CHANGES]: '_goToMergedChanges',
[this.Shortcut.GO_TO_ABANDONED_CHANGES]: '_goToAbandonedChanges',
[this.Shortcut.GO_TO_WATCHED_CHANGES]: '_goToWatchedChanges',
};
},
@ -172,12 +174,16 @@
this.bindShortcut(
this.Shortcut.OPEN_SHORTCUT_HELP_DIALOG, '?');
this.bindShortcut(
this.Shortcut.GO_TO_USER_DASHBOARD, this.GO_KEY, 'i');
this.bindShortcut(
this.Shortcut.GO_TO_OPENED_CHANGES, this.GO_KEY, 'o');
this.bindShortcut(
this.Shortcut.GO_TO_MERGED_CHANGES, this.GO_KEY, 'm');
this.bindShortcut(
this.Shortcut.GO_TO_ABANDONED_CHANGES, this.GO_KEY, 'a');
this.bindShortcut(
this.Shortcut.GO_TO_WATCHED_CHANGES, this.GO_KEY, 'w');
this.bindShortcut(
this.Shortcut.CURSOR_NEXT_CHANGE, 'j');
@ -401,6 +407,10 @@
Gerrit.Nav.navigateToStatusSearch('open');
},
_goToUserDashboard() {
Gerrit.Nav.navigateToUserDashboard();
},
_goToMergedChanges() {
Gerrit.Nav.navigateToStatusSearch('merged');
},
@ -409,6 +419,11 @@
Gerrit.Nav.navigateToStatusSearch('abandoned');
},
_goToWatchedChanges() {
// The query is hardcoded, and doesn't respect custom menu entries
Gerrit.Nav.navigateToSearchQuery('is:watched is:open');
},
_computePluginScreenName({plugin, screen}) {
return Gerrit._getPluginScreenName(plugin, screen);
},

View File

@ -16,6 +16,7 @@ limitations under the License.
-->
<link rel="import" href="/bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../styles/shared-styles.html">
<link rel="import" href="../../../styles/gr-form-styles.html">
<link rel="import" href="../../admin/gr-confirm-delete-item-dialog/gr-confirm-delete-item-dialog.html">
@ -48,9 +49,12 @@ limitations under the License.
.deleteButton:not(.show) {
display: none;
}
.space {
margin-bottom: 1em;
}
</style>
<div class="gr-form-styles">
<fieldset>
<fieldset class="space">
<table>
<thead>
<tr>
@ -80,6 +84,13 @@ limitations under the License.
</tbody>
</table>
</fieldset>
<dom-if if="[[_showLinkAnotherIdentity]]">
<fieldset>
<a href$="[[_computeLinkAnotherIdentity()]]">
<gr-button id="linkAnotherIdentity" link>Link Another Identity</gr-button>
</a>
</fieldset>
</dom-if>
</div>
<gr-overlay id="overlay" with-backdrop>
<gr-confirm-delete-item-dialog

View File

@ -17,6 +17,11 @@
(function() {
'use strict';
const AUTH = [
'OPENID',
'OAUTH',
];
Polymer({
is: 'gr-identities',
_legacyUndefinedCheck: true,
@ -24,8 +29,17 @@
properties: {
_identities: Object,
_idName: String,
serverConfig: Object,
_showLinkAnotherIdentity: {
type: Boolean,
computed: '_computeShowLinkAnotherIdentity(serverConfig)',
},
},
behaviors: [
Gerrit.BaseUrlBehavior,
],
loadData() {
return this.$.restAPI.getExternalIds().then(id => {
this._identities = id;
@ -64,5 +78,24 @@
filterIdentities(item) {
return !item.identity.startsWith('username:');
},
_computeShowLinkAnotherIdentity(config) {
if (config && config.auth &&
config.auth.git_basic_auth_policy) {
return AUTH.includes(
config.auth.git_basic_auth_policy.toUpperCase());
}
return false;
},
_computeLinkAnotherIdentity() {
const baseUrl = this.getBaseUrl() || '';
let pathname = window.location.pathname;
if (baseUrl) {
pathname = '/' + pathname.substring(baseUrl.length);
}
return baseUrl + '/login/' + encodeURIComponent(pathname) + '?link';
},
});
})();

View File

@ -124,5 +124,65 @@ limitations under the License.
MockInteractions.tap(deleteBtn);
assert.isTrue(deleteItem.called);
});
test('_computeShowLinkAnotherIdentity', () => {
let serverConfig;
serverConfig = {
auth: {
git_basic_auth_policy: 'OAUTH',
},
};
assert.isTrue(element._computeShowLinkAnotherIdentity(serverConfig));
serverConfig = {
auth: {
git_basic_auth_policy: 'OpenID',
},
};
assert.isTrue(element._computeShowLinkAnotherIdentity(serverConfig));
serverConfig = {
auth: {
git_basic_auth_policy: 'HTTP_LDAP',
},
};
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
serverConfig = {
auth: {
git_basic_auth_policy: 'LDAP',
},
};
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
serverConfig = {
auth: {
git_basic_auth_policy: 'HTTP',
},
};
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
serverConfig = {};
assert.isFalse(element._computeShowLinkAnotherIdentity(serverConfig));
});
test('_showLinkAnotherIdentity', () => {
element.serverConfig = {
auth: {
git_basic_auth_policy: 'OAUTH',
},
};
assert.isTrue(element._showLinkAnotherIdentity);
element.serverConfig = {
auth: {
git_basic_auth_policy: 'LDAP',
},
};
assert.isFalse(element._showLinkAnotherIdentity);
});
});
</script>

View File

@ -427,7 +427,7 @@ limitations under the License.
</fieldset>
<h2 id="Identities">Identities</h2>
<fieldset>
<gr-identities id="identities"></gr-identities>
<gr-identities id="identities" server-config="[[_serverConfig]]"></gr-identities>
</fieldset>
<template is="dom-if" if="[[_serverConfig.auth.use_contributor_agreements]]">
<h2 id="Agreements">Agreements</h2>

View File

@ -397,19 +397,6 @@ limitations under the License.
});
});
test('installGwt calls _pluginInstalled', () => {
sandbox.stub(Gerrit, '_pluginInstalled');
Gerrit.installGwt('http://test.com/plugins/testplugin/static/test.js');
assert.isTrue(Gerrit._pluginInstalled.calledOnce);
});
test('installGwt returns a plugin', () => {
const plugin = Gerrit.installGwt(
'http://test.com/plugins/testplugin/static/test.js');
assert.isOk(plugin);
assert.isOk(plugin._loadedGwt);
});
test('attributeHelper', () => {
assert.isOk(plugin.attributeHelper());
});

View File

@ -69,9 +69,9 @@
* @return {!Promise}
*/
GrPluginRestApi.prototype.fetch = function(method, url, opt_payload,
opt_errFn) {
opt_errFn, opt_contentType) {
return getRestApi().send(method, this.opt_prefix + url, opt_payload,
opt_errFn);
opt_errFn, opt_contentType);
};
/**
@ -84,20 +84,21 @@
* @return {!Promise} resolves on success, rejects on error.
*/
GrPluginRestApi.prototype.send = function(method, url, opt_payload,
opt_errFn) {
return this.fetch(method, url, opt_payload, opt_errFn).then(response => {
if (response.status < 200 || response.status >= 300) {
return response.text().then(text => {
if (text) {
return Promise.reject(text);
opt_errFn, opt_contentType) {
return this.fetch(method, url, opt_payload, opt_errFn, opt_contentType)
.then(response => {
if (response.status < 200 || response.status >= 300) {
return response.text().then(text => {
if (text) {
return Promise.reject(text);
} else {
return Promise.reject(response.status);
}
});
} else {
return Promise.reject(response.status);
return getRestApi().getResponseObject(response);
}
});
} else {
return getRestApi().getResponseObject(response);
}
});
};
/**
@ -112,16 +113,18 @@
* @param {string} url URL without base path or plugin prefix
* @return {!Promise} resolves on success, rejects on error.
*/
GrPluginRestApi.prototype.post = function(url, opt_payload) {
return this.send('POST', url, opt_payload);
GrPluginRestApi.prototype.post = function(url, opt_payload, opt_errFn,
opt_contentType) {
return this.send('POST', url, opt_payload, opt_errFn, opt_contentType);
};
/**
* @param {string} url URL without base path or plugin prefix
* @return {!Promise} resolves on success, rejects on error.
*/
GrPluginRestApi.prototype.put = function(url, opt_payload) {
return this.send('PUT', url, opt_payload);
GrPluginRestApi.prototype.put = function(url, opt_payload, opt_errFn,
opt_contentType) {
return this.send('PUT', url, opt_payload, opt_errFn, opt_contentType);
};
/**

View File

@ -99,10 +99,6 @@
STYLE: 'style',
};
// GWT JSNI uses $wnd to refer to window.
// http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html
window.$wnd = window;
function flushPreinstalls() {
if (window.Gerrit.flushPreinstalls) {
window.Gerrit.flushPreinstalls();
@ -607,23 +603,6 @@
});
};
/**
* Install "stepping stones" API for GWT-compiled plugins by default.
* @deprecated best effort support, will be removed with GWT UI.
*/
Gerrit.installGwt = function(url) {
const name = getPluginNameFromUrl(url);
let plugin;
try {
plugin = _plugins[name] || new Plugin(url);
plugin.deprecated.install();
Gerrit._pluginInstalled(url);
} catch (e) {
Gerrit._pluginInstallError(`${e.name}: ${e.message}`);
}
return plugin;
};
Gerrit.awaitPluginsLoaded = function() {
if (!_allPluginsPromise) {
if (Gerrit._arePluginsLoaded()) {

View File

@ -77,8 +77,8 @@ def _war_impl(ctx):
# Add lib
transitive_libs = []
for j in ctx.attr.libs:
if hasattr(j, "java"):
transitive_libs.append(j.java.transitive_runtime_deps)
if JavaInfo in j:
transitive_libs.append(j[JavaInfo].transitive_runtime_deps)
elif hasattr(j, "files"):
transitive_libs.append(j.files)
@ -90,7 +90,7 @@ def _war_impl(ctx):
# Add pgm lib
transitive_pgmlibs = []
for j in ctx.attr.pgmlibs:
transitive_pgmlibs.append(j.java.transitive_runtime_deps)
transitive_pgmlibs.append(j[JavaInfo].transitive_runtime_deps)
transitive_pgmlib_deps = depset(transitive = transitive_pgmlibs)
for dep in transitive_pgmlib_deps.to_list():

View File

@ -12,17 +12,12 @@
# 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.
#
from __future__ import print_function
# TODO(davido): use Google style for importing instead:
# import optparse
# ...
# optparse.OptionParser
from optparse import OptionParser
from os import environ, path, makedirs
from subprocess import CalledProcessError, check_call, check_output
from xml.dom import minidom
import argparse
import os
import subprocess
import xml.dom.minidom
import re
import sys
@ -39,30 +34,57 @@ cp_targets = {
MAIN: '//tools/eclipse:main_classpath_collect',
}
ROOT = path.abspath(__file__)
while not path.exists(path.join(ROOT, 'WORKSPACE')):
ROOT = path.dirname(ROOT)
ROOT = os.path.abspath(__file__)
while not os.path.exists(os.path.join(ROOT, 'WORKSPACE')):
ROOT = os.path.dirname(ROOT)
opts = OptionParser()
opts.add_option('--plugins', help='create eclipse projects for plugins',
action='store_true')
opts.add_option('--name', help='name of the generated project',
action='store', default='gerrit', dest='project_name')
opts.add_option('-b', '--batch', action='store_true',
dest='batch', help='Bazel batch option')
opts.add_option('-j', '--java', action='store',
dest='java', help='Post Java 8 support (9)')
opts.add_option('-e', '--edge_java', action='store',
dest='edge_java', help='Post Java 9 support (10|11|...)')
opts.add_option('--bazel', help='name of the bazel executable',
action='store', default='bazel', dest='bazel_exe')
opts = argparse.ArgumentParser("Create Eclipse Project")
opts.add_argument('--plugins', help='create eclipse projects for plugins',
action='store_true')
opts.add_argument('--name', help='name of the generated project',
action='store', default='gerrit', dest='project_name')
opts.add_argument('-b', '--batch', action='store_true',
dest='batch', help='Bazel batch option')
opts.add_argument('-j', '--java', action='store',
dest='java', help='Post Java 8 support (9)')
opts.add_argument('-e', '--edge_java', action='store',
dest='edge_java', help='Post Java 9 support (10|11|...)')
opts.add_argument('--bazel',
help=('name of the bazel executable. Defaults to using'
' bazelisk if found, or bazel if bazelisk is not'
' found.'),
action='store', default=None, dest='bazel_exe')
args = opts.parse_args()
def find_bazel():
if args.bazel_exe:
try:
return subprocess.check_output(
['which', args.bazel_exe]).strip().decode('UTF-8')
except subprocess.CalledProcessError:
print('Bazel command: %s not found' % args.bazel_exe, file=sys.stderr)
sys.exit(1)
try:
return subprocess.check_output(
['which', 'bazelisk']).strip().decode('UTF-8')
except subprocess.CalledProcessError:
try:
return subprocess.check_output(
['which', 'bazel']).strip().decode('UTF-8')
except subprocess.CalledProcessError:
print("Neither bazelisk nor bazel found. Please see"
" Documentation/dev-bazel for instructions on installing"
" one of them.")
sys.exit(1)
args, _ = opts.parse_args()
batch_option = '--batch' if args.batch else None
custom_java = args.java
edge_java = args.edge_java
bazel_exe = args.bazel_exe
bazel_exe = find_bazel()
def _build_bazel_cmd(*args):
build = False
@ -82,23 +104,23 @@ def _build_bazel_cmd(*args):
def retrieve_ext_location():
return check_output(_build_bazel_cmd('info', 'output_base')).strip()
return subprocess.check_output(_build_bazel_cmd('info', 'output_base')).strip()
def gen_bazel_path(ext_location):
bazel = check_output(['which', bazel_exe]).strip().decode('UTF-8')
with open(path.join(ROOT, ".bazel_path"), 'w') as fd:
bazel = subprocess.check_output(['which', bazel_exe]).strip().decode('UTF-8')
with open(os.path.join(ROOT, ".bazel_path"), 'w') as fd:
fd.write("output_base=%s\n" % ext_location)
fd.write("bazel=%s\n" % bazel)
fd.write("PATH=%s\n" % environ["PATH"])
fd.write("PATH=%s\n" % os.environ["PATH"])
def _query_classpath(target):
deps = []
t = cp_targets[target]
try:
check_call(_build_bazel_cmd('build', t))
except CalledProcessError:
subprocess.check_call(_build_bazel_cmd('build', t))
except subprocess.CalledProcessError:
exit(1)
name = 'bazel-bin/tools/eclipse/' + t.split(':')[1] + '.runtime_classpath'
deps = [line.rstrip('\n') for line in open(name)]
@ -106,7 +128,7 @@ def _query_classpath(target):
def gen_project(name='gerrit', root=ROOT):
p = path.join(root, '.project')
p = os.path.join(root, '.project')
with open(p, 'w') as fd:
print("""\
<?xml version="1.0" encoding="UTF-8"?>
@ -125,9 +147,9 @@ def gen_project(name='gerrit', root=ROOT):
def gen_plugin_classpath(root):
p = path.join(root, '.classpath')
p = os.path.join(root, '.classpath')
with open(p, 'w') as fd:
if path.exists(path.join(root, 'src', 'test', 'java')):
if os.path.exists(os.path.join(root, 'src', 'test', 'java')):
testpath = """
<classpathentry excluding="**/BUILD" kind="src" path="src/test/java"\
out="eclipse-out/test">
@ -147,7 +169,7 @@ def gen_plugin_classpath(root):
def gen_classpath(ext):
def make_classpath():
impl = minidom.getDOMImplementation()
impl = xml.dom.minidom.getDOMImplementation()
return impl.createDocument(None, 'classpath', None)
def classpathentry(kind, path, src=None, out=None, exported=None):
@ -222,7 +244,7 @@ def gen_classpath(ext):
"external/bazel_tools/tools/jdk/TestRunner_deploy.jar"):
continue
if p.startswith("external"):
p = path.join(ext, p)
p = os.path.join(ext, p)
lib.add(p)
classpathentry('src', 'java')
@ -239,11 +261,11 @@ def gen_classpath(ext):
continue
out = 'eclipse-out/' + s
p = path.join(s, 'java')
if path.exists(p):
p = os.path.join(s, 'java')
if os.path.exists(p):
classpathentry('src', p, out=out + '/main')
p = path.join(s, 'javatests')
if path.exists(p):
p = os.path.join(s, 'javatests')
if os.path.exists(p):
classpathentry('src', p, out=out + '/test')
continue
@ -255,8 +277,8 @@ def gen_classpath(ext):
o = 'eclipse-out/test'
for srctype in ['java', 'resources']:
p = path.join(s, 'src', env, srctype)
if path.exists(p):
p = os.path.join(s, 'src', env, srctype)
if os.path.exists(p):
classpathentry('src', p, out=o)
for libs in [lib]:
@ -266,8 +288,8 @@ def gen_classpath(ext):
if m:
prefix = m.group(1)
suffix = m.group(2)
p = path.join(prefix, "jar", "%s-src.jar" % suffix)
if path.exists(p):
p = os.path.join(prefix, "jar", "%s-src.jar" % suffix)
if os.path.exists(p):
s = p
if args.plugins:
classpathentry('lib', j, s, exported=True)
@ -285,13 +307,13 @@ def gen_classpath(ext):
classpathentry('src', '.apt_generated')
classpathentry('src', '.apt_generated_tests', out="eclipse-out/test")
p = path.join(ROOT, '.classpath')
p = os.path.join(ROOT, '.classpath')
with open(p, 'w') as fd:
doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8')
if args.plugins:
for plugin in plugins:
plugindir = path.join(ROOT, plugin)
plugindir = os.path.join(ROOT, plugin)
try:
gen_project(plugin.replace('plugins/', ""), plugindir)
gen_plugin_classpath(plugindir)
@ -301,17 +323,17 @@ def gen_classpath(ext):
def gen_factorypath(ext):
doc = minidom.getDOMImplementation().createDocument(None, 'factorypath',
None)
doc = xml.dom.minidom.getDOMImplementation().createDocument(
None, 'factorypath', None)
for jar in _query_classpath(AUTO):
e = doc.createElement('factorypathentry')
e.setAttribute('kind', 'EXTJAR')
e.setAttribute('id', path.join(ext, jar))
e.setAttribute('id', os.path.join(ext, jar))
e.setAttribute('enabled', 'true')
e.setAttribute('runInBatchMode', 'false')
doc.documentElement.appendChild(e)
p = path.join(ROOT, '.factorypath')
p = os.path.join(ROOT, '.factorypath')
with open(p, 'w') as fd:
doc.writexml(fd, addindent='\t', newl='\n', encoding='UTF-8')
@ -324,8 +346,8 @@ try:
gen_bazel_path(ext_location)
try:
check_call(_build_bazel_cmd('build', MAIN))
except CalledProcessError:
subprocess.check_call(_build_bazel_cmd('build', MAIN))
except subprocess.CalledProcessError:
exit(1)
except KeyboardInterrupt:
print('Interrupted by user', file=sys.stderr)