| // Copyright 2008 Google Inc. |
| // |
| // 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.pgm; |
| |
| import com.google.gerrit.client.reviewdb.Account; |
| import com.google.gerrit.client.reviewdb.AccountGroup; |
| import com.google.gerrit.client.reviewdb.AccountGroupMember; |
| import com.google.gerrit.client.reviewdb.ApprovalCategory; |
| import com.google.gerrit.client.reviewdb.Change; |
| import com.google.gerrit.client.reviewdb.ChangeApproval; |
| import com.google.gerrit.client.reviewdb.PatchSet; |
| import com.google.gerrit.client.reviewdb.Project; |
| import com.google.gerrit.client.reviewdb.ProjectRight; |
| import com.google.gerrit.client.reviewdb.ReviewDb; |
| import com.google.gerrit.client.reviewdb.SystemConfig; |
| import com.google.gerrit.client.rpc.Common; |
| import com.google.gerrit.git.InvalidRepositoryException; |
| import com.google.gerrit.git.PatchSetImporter; |
| import com.google.gerrit.server.ChangeUtil; |
| import com.google.gerrit.server.GerritServer; |
| import com.google.gwtjsonrpc.server.XsrfException; |
| import com.google.gwtorm.client.OrmException; |
| import com.google.gwtorm.jdbc.JdbcSchema; |
| |
| import org.spearce.jgit.lib.Constants; |
| import org.spearce.jgit.lib.LockFile; |
| import org.spearce.jgit.lib.ObjectId; |
| import org.spearce.jgit.lib.ProgressMonitor; |
| import org.spearce.jgit.lib.Ref; |
| import org.spearce.jgit.lib.RefUpdate; |
| import org.spearce.jgit.lib.Repository; |
| import org.spearce.jgit.lib.TextProgressMonitor; |
| import org.spearce.jgit.revwalk.RevCommit; |
| import org.spearce.jgit.revwalk.RevWalk; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.sql.Connection; |
| import java.sql.PreparedStatement; |
| import java.sql.SQLException; |
| import java.sql.Statement; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Imports data from Gerrit 1 into Gerrit 2. |
| * <p> |
| * The tool assumes that <code>devutil/import_gerrit1.sql</code> has already |
| * been executed on this schema. All existing ProjectRight entities are wiped |
| * from the database and generated from scratch. |
| * <p> |
| * The tool requires Gerrit 1 tables (<code>gerrit1.$table_name</code>) through |
| * the same database connection as the ReviewDb schema is on. |
| */ |
| public class ImportGerrit1 { |
| private static GerritServer gs; |
| private static ReviewDb db; |
| private static Connection sql; |
| private static ApprovalCategory verifyCategory; |
| private static ApprovalCategory approveCategory; |
| private static ApprovalCategory submitCategory; |
| |
| public static void main(final String[] argv) throws OrmException, |
| XsrfException, SQLException, IOException, InvalidRepositoryException { |
| final ProgressMonitor pm = new TextProgressMonitor(); |
| gs = GerritServer.getInstance(); |
| db = Common.getSchemaFactory().open(); |
| sql = ((JdbcSchema) db).getConnection(); |
| try { |
| verifyCategory = db.approvalCategories().byName("Verified"); |
| approveCategory = db.approvalCategories().byName("Code Review"); |
| submitCategory = db.approvalCategories().get(ApprovalCategory.SUBMIT); |
| |
| final Statement query = sql.createStatement(); |
| java.sql.ResultSet srcs; |
| |
| // Set project descriptions back into git |
| // |
| pm.start(1); |
| pm.beginTask("Export project descriptions", ProgressMonitor.UNKNOWN); |
| srcs = query.executeQuery("SELECT p.project_id FROM projects p"); |
| while (srcs.next()) { |
| final Project.Id projectId = new Project.Id(srcs.getInt(1)); |
| if (ProjectRight.WILD_PROJECT.equals(projectId)) { |
| continue; |
| } |
| final Project proj = db.projects().get(projectId); |
| final Repository e; |
| final LockFile f; |
| |
| e = gs.getRepositoryCache().get(proj.getName()); |
| f = new LockFile(new File(e.getDirectory(), "description")); |
| if (f.lock()) { |
| String d = proj.getDescription(); |
| if (d != null) { |
| d = d.trim() + "\n"; |
| } else { |
| d = ""; |
| } |
| f.write(Constants.encode(d)); |
| f.commit(); |
| } else { |
| throw new IOException("Cannot set description for " + proj.getName()); |
| } |
| pm.update(1); |
| } |
| srcs.close(); |
| pm.endTask(); |
| |
| // Convert the approval right data from projects. |
| // |
| pm.start(1); |
| pm.beginTask("Import project rights", ProgressMonitor.UNKNOWN); |
| query.executeUpdate("DELETE FROM project_rights"); |
| insertApprovalWildCard(); |
| insertAccessWildCard(); |
| srcs = |
| query.executeQuery("SELECT p.project_id, r.ar_key" |
| + " FROM gerrit1.project_code_reviews r, projects p" |
| + " WHERE p.project_id = r.project_id"); |
| while (srcs.next()) { |
| final Project.Id projectId = new Project.Id(srcs.getInt(1)); |
| final String arKey = srcs.getString(2); |
| doImport(projectId, arKey); |
| pm.update(1); |
| } |
| srcs.close(); |
| pm.endTask(); |
| |
| // Compute the sort keys for change entities |
| // |
| srcs = query.executeQuery("SELECT change_id FROM changes"); |
| final ArrayList<Change.Id> changesToDo = new ArrayList<Change.Id>(); |
| while (srcs.next()) { |
| final Change.Id changeId = new Change.Id(srcs.getInt(1)); |
| changesToDo.add(changeId); |
| } |
| srcs.close(); |
| pm.start(1); |
| pm.beginTask("Compute sort keys", changesToDo.size()); |
| for (final Change.Id changeId : changesToDo) { |
| final Change c = db.changes().get(changeId); |
| ChangeUtil.computeSortKey(c); |
| final List<ChangeApproval> approvals = |
| db.changeApprovals().byChange(changeId).toList(); |
| for (final ChangeApproval a : approvals) { |
| a.cache(c); |
| } |
| db.changeApprovals().update(approvals); |
| db.changes().update(Collections.singleton(c)); |
| pm.update(1); |
| } |
| pm.endTask(); |
| |
| // Rebuild the cached PatchSet information directly from Git. |
| // There's some oddities in the Gerrit 1 data that we got from |
| // Google App Engine's data store; the quickest way to fix it |
| // is to just recache the data from Git. |
| // |
| srcs = |
| query.executeQuery("SELECT change_id,patch_set_id FROM patch_sets"); |
| final ArrayList<PatchSet.Id> psToDo = new ArrayList<PatchSet.Id>(); |
| while (srcs.next()) { |
| final Change.Id changeId = new Change.Id(srcs.getInt(1)); |
| final PatchSet.Id psId = new PatchSet.Id(changeId, srcs.getInt(2)); |
| psToDo.add(psId); |
| } |
| query.close(); |
| |
| final Map<String, Set<String>> validRefs = |
| new HashMap<String, Set<String>>(); |
| pm.start(1); |
| pm.beginTask("Import patch sets", psToDo.size()); |
| for (final PatchSet.Id psId : psToDo) { |
| final Change c = db.changes().get(psId.getParentKey()); |
| final PatchSet ps = db.patchSets().get(psId); |
| final String projectName = c.getDest().getParentKey().get(); |
| final Repository repo = gs.getRepositoryCache().get(projectName); |
| final RevWalk rw = new RevWalk(repo); |
| final RevCommit src = |
| rw.parseCommit(ObjectId.fromString(ps.getRevision().get())); |
| new PatchSetImporter(db, repo, src, ps, false).run(); |
| Set<String> s = validRefs.get(projectName); |
| if (s == null) { |
| s = new HashSet<String>(); |
| validRefs.put(projectName, s); |
| } |
| s.add(ps.getRefName()); |
| pm.update(1); |
| } |
| pm.endTask(); |
| |
| pruneProjectRefs(pm, validRefs); |
| } finally { |
| db.close(); |
| } |
| } |
| |
| private static void pruneProjectRefs(final ProgressMonitor pm, |
| final Map<String, Set<String>> validRefs) throws OrmException, |
| IOException { |
| final List<Project> pToClean = db.projects().all().toList(); |
| pm.start(1); |
| pm.beginTask("Prune old refs", pToClean.size()); |
| for (final Project p : pToClean) { |
| final Repository repo; |
| try { |
| repo = gs.getRepositoryCache().get(p.getName()); |
| } catch (InvalidRepositoryException e) { |
| pm.update(1); |
| continue; |
| } |
| |
| Set<String> valid = validRefs.get(p.getName()); |
| if (valid == null) { |
| valid = Collections.emptySet(); |
| } |
| |
| final RevWalk rw = new RevWalk(repo); |
| for (final Ref r : repo.getAllRefs().values()) { |
| boolean delete = false; |
| if (r.getName().startsWith("refs/merges/")) { |
| delete = true; |
| } |
| if (r.getName().startsWith("refs/changes/") |
| && !valid.contains(r.getName())) { |
| delete = true; |
| } |
| if (delete) { |
| final RefUpdate u = repo.updateRef(r.getName()); |
| u.setForceUpdate(true); |
| u.delete(rw); |
| } |
| } |
| pm.update(1); |
| } |
| pm.endTask(); |
| } |
| |
| private static void insertApprovalWildCard() throws OrmException { |
| final ProjectRight.Key key = |
| new ProjectRight.Key(ProjectRight.WILD_PROJECT, |
| approveCategory.getId(), db.systemConfig().get( |
| new SystemConfig.Key()).registeredGroupId); |
| final ProjectRight pr = new ProjectRight(key); |
| pr.setMinValue((short) -1); |
| pr.setMaxValue((short) 1); |
| db.projectRights().insert(Collections.singleton(pr)); |
| } |
| |
| private static void insertAccessWildCard() throws OrmException { |
| final ProjectRight.Key key = |
| new ProjectRight.Key(ProjectRight.WILD_PROJECT, ApprovalCategory.READ, |
| db.systemConfig().get(new SystemConfig.Key()).anonymousGroupId); |
| final ProjectRight pr = new ProjectRight(key); |
| pr.setMinValue((short) 1); |
| pr.setMaxValue((short) 1); |
| db.projectRights().insert(Collections.singleton(pr)); |
| } |
| |
| private static void doImport(final Project.Id projectId, final String arKey) |
| throws OrmException, SQLException { |
| final int arId = findId(arKey); |
| if (arId < 0) { |
| return; |
| } |
| |
| final Project proj = db.projects().get(projectId); |
| final Set<AccountGroup.Id> approverg = groups(arId, "approver"); |
| final Set<AccountGroup.Id> verifierg = groups(arId, "verifier"); |
| final Set<AccountGroup.Id> submitterg = groups(arId, "submitter"); |
| |
| final Set<Account.Id> approveru = users(arId, "approver"); |
| final Set<Account.Id> verifieru = users(arId, "verifier"); |
| final Set<Account.Id> submitteru = users(arId, "submitter"); |
| |
| importCat(proj, "approvers", approveCategory, approverg, approveru); |
| importCat(proj, "verifiers", verifyCategory, verifierg, verifieru); |
| importCat(proj, "submitters", submitCategory, submitterg, submitteru); |
| } |
| |
| private static void importCat(final Project proj, final String type, |
| final ApprovalCategory category, final Set<AccountGroup.Id> groups, |
| final Set<Account.Id> users) throws OrmException { |
| final HashSet<Account.Id> needGroup = new HashSet<Account.Id>(users); |
| for (final AccountGroup.Id groupId : groups) { |
| insertRight(proj, category, groupId); |
| for (final Iterator<Account.Id> i = needGroup.iterator(); i.hasNext();) { |
| if (Common.getGroupCache().isInGroup(i.next(), groupId)) { |
| i.remove(); |
| } |
| } |
| } |
| |
| if (!needGroup.isEmpty()) { |
| final AccountGroup.Id groupId = |
| new AccountGroup.Id(db.nextAccountGroupId()); |
| final AccountGroup group = |
| new AccountGroup(new AccountGroup.NameKey(shortName(proj) + "_" |
| + proj.getId().get() + "-" + type), groupId); |
| group.setOwnerGroupId(proj.getOwnerGroupId()); |
| group.setDescription(proj.getName() + " " + type); |
| db.accountGroups().insert(Collections.singleton(group)); |
| for (final Account.Id aId : needGroup) { |
| db.accountGroupMembers().insert( |
| Collections.singleton(new AccountGroupMember( |
| new AccountGroupMember.Key(aId, groupId)))); |
| } |
| insertRight(proj, category, groupId); |
| } |
| } |
| |
| private static void insertRight(final Project proj, |
| final ApprovalCategory category, final AccountGroup.Id groupId) |
| throws OrmException { |
| final ProjectRight.Key key = |
| new ProjectRight.Key(proj.getId(), category.getId(), groupId); |
| final ProjectRight pr = new ProjectRight(key); |
| if (category == approveCategory) { |
| pr.setMinValue((short) -2); |
| pr.setMaxValue((short) 2); |
| } else if (category == verifyCategory) { |
| pr.setMinValue((short) -1); |
| pr.setMaxValue((short) 1); |
| } else if (category == submitCategory) { |
| pr.setMinValue((short) 1); |
| pr.setMaxValue((short) 1); |
| } else { |
| throw new OrmException("Cannot import category " + category.getId()); |
| } |
| db.projectRights().insert(Collections.singleton(pr)); |
| } |
| |
| private static String shortName(final Project proj) { |
| final String n = proj.getName(); |
| final int s = n.lastIndexOf('/'); |
| return 0 < s ? n.substring(s + 1) : n; |
| } |
| |
| private static Set<AccountGroup.Id> groups(final int arId, final String type) |
| throws SQLException { |
| final PreparedStatement ps = |
| sql.prepareStatement("SELECT g.group_id FROM account_groups g," |
| + " gerrit1.approval_right_groups s, gerrit1.account_groups o" |
| + " WHERE s.ar_id = ? AND s.type = ?" |
| + " AND o.gae_key = s.group_key AND (g.name = o.name" |
| + " OR (g.name = 'Administrators' AND o.name = 'admin'))"); |
| try { |
| ps.setInt(1, arId); |
| ps.setString(2, type); |
| final java.sql.ResultSet rs = ps.executeQuery(); |
| try { |
| final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>(); |
| while (rs.next()) { |
| r.add(new AccountGroup.Id(rs.getInt(1))); |
| } |
| return r; |
| } finally { |
| rs.close(); |
| } |
| } finally { |
| ps.close(); |
| } |
| } |
| |
| private static Set<Account.Id> users(final int arId, final String type) |
| throws SQLException { |
| final PreparedStatement ps = |
| sql.prepareStatement("SELECT a.account_id FROM accounts a," |
| + " gerrit1.approval_right_users s" |
| + " WHERE s.ar_id = ? AND s.type = ?" |
| + " AND a.preferred_email = s.email"); |
| try { |
| ps.setInt(1, arId); |
| ps.setString(2, type); |
| final java.sql.ResultSet rs = ps.executeQuery(); |
| try { |
| final HashSet<Account.Id> r = new HashSet<Account.Id>(); |
| while (rs.next()) { |
| r.add(new Account.Id(rs.getInt(1))); |
| } |
| return r; |
| } finally { |
| rs.close(); |
| } |
| } finally { |
| ps.close(); |
| } |
| } |
| |
| private static int findId(final String arKey) throws SQLException { |
| final PreparedStatement ps = |
| sql.prepareStatement("SELECT ar_id FROM gerrit1.approval_rights" |
| + " WHERE gae_key=?"); |
| try { |
| ps.setString(1, arKey); |
| final java.sql.ResultSet rs = ps.executeQuery(); |
| try { |
| if (rs.next()) { |
| return rs.getInt(1); |
| } |
| return -1; |
| } finally { |
| rs.close(); |
| } |
| } finally { |
| ps.close(); |
| } |
| } |
| } |