Search in sources :

Example 36 with TicketModel

use of com.gitblit.models.TicketModel in project gitblit by gitblit.

the class PatchsetReceivePack method processPatchset.

/**
	 * Creates or updates an ticket with the specified patchset.
	 *
	 * @param cmd
	 * @return a ticket if the creation or update was successful
	 */
private TicketModel processPatchset(PatchsetCommand cmd) {
    Change change = cmd.getChange();
    if (cmd.isNewTicket()) {
        // create the ticket object
        TicketModel ticket = ticketService.createTicket(repository, cmd.getTicketId(), change);
        if (ticket != null) {
            sendInfo("");
            sendHeader("#{0,number,0}: {1}", ticket.number, StringUtils.trimString(ticket.title, Constants.LEN_SHORTLOG));
            sendInfo("created proposal ticket from patchset");
            sendInfo(ticketService.getTicketUrl(ticket));
            sendInfo("");
            // log the new patch ref
            RefLogUtils.updateRefLog(user, getRepository(), Arrays.asList(new ReceiveCommand(cmd.getOldId(), cmd.getNewId(), cmd.getRefName())));
            // call any patchset hooks
            for (PatchsetHook hook : gitblit.getExtensions(PatchsetHook.class)) {
                try {
                    hook.onNewPatchset(ticket);
                } catch (Exception e) {
                    LOGGER.error("Failed to execute extension", e);
                }
            }
            return ticket;
        } else {
            sendError("FAILED to create ticket");
        }
    } else {
        // update an existing ticket
        TicketModel ticket = ticketService.updateTicket(repository, cmd.getTicketId(), change);
        if (ticket != null) {
            sendInfo("");
            sendHeader("#{0,number,0}: {1}", ticket.number, StringUtils.trimString(ticket.title, Constants.LEN_SHORTLOG));
            if (change.patchset.rev == 1) {
                // new patchset
                sendInfo("uploaded patchset {0} ({1})", change.patchset.number, change.patchset.type.toString());
            } else {
                // updated patchset
                sendInfo("added {0} {1} to patchset {2}", change.patchset.added, change.patchset.added == 1 ? "commit" : "commits", change.patchset.number);
            }
            sendInfo(ticketService.getTicketUrl(ticket));
            sendInfo("");
            // log the new patchset ref
            RefLogUtils.updateRefLog(user, getRepository(), Arrays.asList(new ReceiveCommand(cmd.getOldId(), cmd.getNewId(), cmd.getRefName())));
            // call any patchset hooks
            final boolean isNewPatchset = change.patchset.rev == 1;
            for (PatchsetHook hook : gitblit.getExtensions(PatchsetHook.class)) {
                try {
                    if (isNewPatchset) {
                        hook.onNewPatchset(ticket);
                    } else {
                        hook.onUpdatePatchset(ticket);
                    }
                } catch (Exception e) {
                    LOGGER.error("Failed to execute extension", e);
                }
            }
            // return the updated ticket
            return ticket;
        } else {
            sendError("FAILED to upload {0} for ticket {1,number,0}", change.patchset, cmd.getTicketId());
        }
    }
    return null;
}
Also used : ReceiveCommand(org.eclipse.jgit.transport.ReceiveCommand) PatchsetHook(com.gitblit.extensions.PatchsetHook) TicketModel(com.gitblit.models.TicketModel) Change(com.gitblit.models.TicketModel.Change) IOException(java.io.IOException)

Example 37 with TicketModel

use of com.gitblit.models.TicketModel in project gitblit by gitblit.

the class PatchsetReceivePack method executeCommands.

/** Execute commands to update references. */
@Override
protected void executeCommands() {
    // we process patchsets unless the user is pushing something special
    boolean processPatchsets = true;
    for (ReceiveCommand cmd : filterCommands(Result.NOT_ATTEMPTED)) {
        if (ticketService instanceof BranchTicketService && BranchTicketService.BRANCH.equals(cmd.getRefName())) {
            // the user is pushing an update to the BranchTicketService data
            processPatchsets = false;
        }
    }
    // reset the patchset refs to NOT_ATTEMPTED (see validateCommands)
    for (ReceiveCommand cmd : filterCommands(Result.OK)) {
        if (isPatchsetRef(cmd.getRefName())) {
            cmd.setResult(Result.NOT_ATTEMPTED);
        } else if (ticketService instanceof BranchTicketService && BranchTicketService.BRANCH.equals(cmd.getRefName())) {
            // the user is pushing an update to the BranchTicketService data
            processPatchsets = false;
        }
    }
    List<ReceiveCommand> toApply = filterCommands(Result.NOT_ATTEMPTED);
    if (toApply.isEmpty()) {
        return;
    }
    ProgressMonitor updating = NullProgressMonitor.INSTANCE;
    boolean sideBand = isCapabilityEnabled(CAPABILITY_SIDE_BAND_64K);
    if (sideBand) {
        SideBandProgressMonitor pm = new SideBandProgressMonitor(msgOut);
        pm.setDelayStart(250, TimeUnit.MILLISECONDS);
        updating = pm;
    }
    BatchRefUpdate batch = getRepository().getRefDatabase().newBatchUpdate();
    batch.setAllowNonFastForwards(isAllowNonFastForwards());
    batch.setRefLogIdent(getRefLogIdent());
    batch.setRefLogMessage("push", true);
    ReceiveCommand patchsetRefCmd = null;
    PatchsetCommand patchsetCmd = null;
    for (ReceiveCommand cmd : toApply) {
        if (Result.NOT_ATTEMPTED != cmd.getResult()) {
            // Already rejected by the core receive process.
            continue;
        }
        if (isPatchsetRef(cmd.getRefName()) && processPatchsets) {
            if (ticketService == null) {
                sendRejection(cmd, "Sorry, the ticket service is unavailable and can not accept patchsets at this time.");
                continue;
            }
            if (!ticketService.isReady()) {
                sendRejection(cmd, "Sorry, the ticket service can not accept patchsets at this time.");
                continue;
            }
            if (UserModel.ANONYMOUS.equals(user)) {
                // server allows anonymous pushes, but anonymous patchset
                // contributions are prohibited by design
                sendRejection(cmd, "Sorry, anonymous patchset contributions are prohibited.");
                continue;
            }
            final Matcher m = NEW_PATCHSET.matcher(cmd.getRefName());
            if (m.matches()) {
                // prohibit pushing directly to a patchset ref
                long id = getTicketId(cmd.getRefName());
                sendError("You may not directly push directly to a patchset ref!");
                sendError("Instead, please push to one the following:");
                sendError(" - {0}{1,number,0}", Constants.R_FOR, id);
                sendError(" - {0}{1,number,0}", Constants.R_TICKET, id);
                sendRejection(cmd, "protected ref");
                continue;
            }
            if (hasRefNamespace(Constants.R_FOR)) {
                // the refs/for/ namespace exists and it must not
                LOGGER.error("{} already has refs in the {} namespace", repository.name, Constants.R_FOR);
                sendRejection(cmd, "Sorry, a repository administrator will have to remove the {} namespace", Constants.R_FOR);
                continue;
            }
            if (cmd.getNewId().equals(ObjectId.zeroId())) {
                // ref deletion request
                if (cmd.getRefName().startsWith(Constants.R_TICKET)) {
                    if (user.canDeleteRef(repository)) {
                        batch.addCommand(cmd);
                    } else {
                        sendRejection(cmd, "Sorry, you do not have permission to delete {}", cmd.getRefName());
                    }
                } else {
                    sendRejection(cmd, "Sorry, you can not delete {}", cmd.getRefName());
                }
                continue;
            }
            if (patchsetRefCmd != null) {
                sendRejection(cmd, "You may only push one patchset at a time.");
                continue;
            }
            LOGGER.info(MessageFormat.format("Verifying {0} push ref \"{1}\" received from {2}", repository.name, cmd.getRefName(), user.username));
            // responsible verification
            String responsible = PatchsetCommand.getSingleOption(cmd, PatchsetCommand.RESPONSIBLE);
            if (!StringUtils.isEmpty(responsible)) {
                UserModel assignee = gitblit.getUserModel(responsible);
                if (assignee == null) {
                    // no account by this name
                    sendRejection(cmd, "{0} can not be assigned any tickets because there is no user account by that name", responsible);
                    continue;
                } else if (!assignee.canPush(repository)) {
                    // account does not have RW permissions
                    sendRejection(cmd, "{0} ({1}) can not be assigned any tickets because the user does not have RW permissions for {2}", assignee.getDisplayName(), assignee.username, repository.name);
                    continue;
                }
            }
            // milestone verification
            String milestone = PatchsetCommand.getSingleOption(cmd, PatchsetCommand.MILESTONE);
            if (!StringUtils.isEmpty(milestone)) {
                TicketMilestone milestoneModel = ticketService.getMilestone(repository, milestone);
                if (milestoneModel == null) {
                    // milestone does not exist
                    sendRejection(cmd, "Sorry, \"{0}\" is not a valid milestone!", milestone);
                    continue;
                }
            }
            // watcher verification
            List<String> watchers = PatchsetCommand.getOptions(cmd, PatchsetCommand.WATCH);
            if (!ArrayUtils.isEmpty(watchers)) {
                boolean verified = true;
                for (String watcher : watchers) {
                    UserModel user = gitblit.getUserModel(watcher);
                    if (user == null) {
                        // watcher does not exist
                        sendRejection(cmd, "Sorry, \"{0}\" is not a valid username for the watch list!", watcher);
                        verified = false;
                        break;
                    }
                }
                if (!verified) {
                    continue;
                }
            }
            patchsetRefCmd = cmd;
            patchsetCmd = preparePatchset(cmd);
            if (patchsetCmd != null) {
                batch.addCommand(patchsetCmd);
            }
            continue;
        }
        batch.addCommand(cmd);
    }
    if (!batch.getCommands().isEmpty()) {
        try {
            batch.execute(getRevWalk(), updating);
        } catch (IOException err) {
            for (ReceiveCommand cmd : toApply) {
                if (cmd.getResult() == Result.NOT_ATTEMPTED) {
                    sendRejection(cmd, "lock error: {0}", err.getMessage());
                    LOGGER.error(MessageFormat.format("failed to lock {0}:{1}", repository.name, cmd.getRefName()), err);
                }
            }
        }
    }
    //
    if (patchsetRefCmd != null && patchsetCmd != null) {
        if (!patchsetCmd.getResult().equals(Result.OK)) {
            // patchset command failed!
            LOGGER.error(patchsetCmd.getType() + " " + patchsetCmd.getRefName() + " " + patchsetCmd.getResult());
            patchsetRefCmd.setResult(patchsetCmd.getResult(), patchsetCmd.getMessage());
        } else {
            // all patchset commands were applied
            patchsetRefCmd.setResult(Result.OK);
            // update the ticket branch ref
            RefUpdate ru = updateRef(patchsetCmd.getTicketBranch(), patchsetCmd.getNewId(), patchsetCmd.getPatchsetType());
            updateReflog(ru);
            TicketModel ticket = processPatchset(patchsetCmd);
            if (ticket != null) {
                ticketNotifier.queueMailing(ticket);
            }
        }
    }
    //
    // if there are standard ref update receive commands that were
    // successfully processed, process referenced tickets, if any
    //
    List<ReceiveCommand> allUpdates = ReceiveCommand.filter(batch.getCommands(), Result.OK);
    List<ReceiveCommand> refUpdates = excludePatchsetCommands(allUpdates);
    List<ReceiveCommand> stdUpdates = excludeTicketCommands(refUpdates);
    if (!stdUpdates.isEmpty()) {
        int ticketsProcessed = 0;
        for (ReceiveCommand cmd : stdUpdates) {
            switch(cmd.getType()) {
                case CREATE:
                case UPDATE:
                    if (cmd.getRefName().startsWith(Constants.R_HEADS)) {
                        Collection<TicketModel> tickets = processReferencedTickets(cmd);
                        ticketsProcessed += tickets.size();
                        for (TicketModel ticket : tickets) {
                            ticketNotifier.queueMailing(ticket);
                        }
                    }
                    break;
                case UPDATE_NONFASTFORWARD:
                    if (cmd.getRefName().startsWith(Constants.R_HEADS)) {
                        String base = JGitUtils.getMergeBase(getRepository(), cmd.getOldId(), cmd.getNewId());
                        List<TicketLink> deletedRefs = JGitUtils.identifyTicketsBetweenCommits(getRepository(), settings, base, cmd.getOldId().name());
                        for (TicketLink link : deletedRefs) {
                            link.isDelete = true;
                        }
                        Change deletion = new Change(user.username);
                        deletion.pendingLinks = deletedRefs;
                        ticketService.updateTicket(repository, 0, deletion);
                        Collection<TicketModel> tickets = processReferencedTickets(cmd);
                        ticketsProcessed += tickets.size();
                        for (TicketModel ticket : tickets) {
                            ticketNotifier.queueMailing(ticket);
                        }
                    }
                    break;
                default:
                    break;
            }
        }
        if (ticketsProcessed == 1) {
            sendInfo("1 ticket updated");
        } else if (ticketsProcessed > 1) {
            sendInfo("{0} tickets updated", ticketsProcessed);
        }
    }
    // reset the ticket caches for the repository
    ticketService.resetCaches(repository);
}
Also used : ReceiveCommand(org.eclipse.jgit.transport.ReceiveCommand) BranchTicketService(com.gitblit.tickets.BranchTicketService) Matcher(java.util.regex.Matcher) TicketModel(com.gitblit.models.TicketModel) IOException(java.io.IOException) Change(com.gitblit.models.TicketModel.Change) NullProgressMonitor(org.eclipse.jgit.lib.NullProgressMonitor) ProgressMonitor(org.eclipse.jgit.lib.ProgressMonitor) UserModel(com.gitblit.models.UserModel) TicketLink(com.gitblit.models.TicketModel.TicketLink) BatchRefUpdate(org.eclipse.jgit.lib.BatchRefUpdate) TicketMilestone(com.gitblit.tickets.TicketMilestone) RefUpdate(org.eclipse.jgit.lib.RefUpdate) BatchRefUpdate(org.eclipse.jgit.lib.BatchRefUpdate)

Example 38 with TicketModel

use of com.gitblit.models.TicketModel in project gitblit by gitblit.

the class BranchTicketService method onRefsChanged.

/**
	 * Listen for tickets branch changes and (re)index tickets, as appropriate
	 */
@Override
public synchronized void onRefsChanged(RefsChangedEvent event) {
    if (!(event instanceof ReceiveCommandEvent)) {
        return;
    }
    ReceiveCommandEvent branchUpdate = (ReceiveCommandEvent) event;
    RepositoryModel repository = branchUpdate.model;
    ReceiveCommand cmd = branchUpdate.cmd;
    try {
        switch(cmd.getType()) {
            case CREATE:
            case UPDATE_NONFASTFORWARD:
                // reindex everything
                reindex(repository);
                break;
            case UPDATE:
                // incrementally index ticket updates
                resetCaches(repository);
                long start = System.nanoTime();
                log.info("incrementally indexing {} ticket branch due to received ref update", repository.name);
                Repository db = repositoryManager.getRepository(repository.name);
                try {
                    Set<Long> ids = new HashSet<Long>();
                    List<PathChangeModel> paths = JGitUtils.getFilesInRange(db, cmd.getOldId().getName(), cmd.getNewId().getName());
                    for (PathChangeModel path : paths) {
                        String name = path.name.substring(path.name.lastIndexOf('/') + 1);
                        if (!JOURNAL.equals(name)) {
                            continue;
                        }
                        String tid = path.path.split("/")[2];
                        long ticketId = Long.parseLong(tid);
                        if (!ids.contains(ticketId)) {
                            ids.add(ticketId);
                            TicketModel ticket = getTicket(repository, ticketId);
                            log.info(MessageFormat.format("indexing ticket #{0,number,0}: {1}", ticketId, ticket.title));
                            indexer.index(ticket);
                        }
                    }
                    long end = System.nanoTime();
                    log.info("incremental indexing of {0} ticket(s) completed in {1} msecs", ids.size(), TimeUnit.NANOSECONDS.toMillis(end - start));
                } finally {
                    db.close();
                }
                break;
            default:
                log.warn("Unexpected receive type {} in BranchTicketService.onRefsChanged" + cmd.getType());
                break;
        }
    } catch (Exception e) {
        log.error("failed to reindex " + repository.name, e);
    }
}
Also used : ReceiveCommand(org.eclipse.jgit.transport.ReceiveCommand) PathChangeModel(com.gitblit.models.PathModel.PathChangeModel) TicketModel(com.gitblit.models.TicketModel) RepositoryModel(com.gitblit.models.RepositoryModel) ConcurrentRefUpdateException(org.eclipse.jgit.api.errors.ConcurrentRefUpdateException) IOException(java.io.IOException) Repository(org.eclipse.jgit.lib.Repository) ReceiveCommandEvent(com.gitblit.git.ReceiveCommandEvent) AtomicLong(java.util.concurrent.atomic.AtomicLong) HashSet(java.util.HashSet)

Example 39 with TicketModel

use of com.gitblit.models.TicketModel in project gitblit by gitblit.

the class FileTicketService method getTickets.

/**
	 * Returns all the tickets in the repository. Querying tickets from the
	 * repository requires deserializing all tickets. This is an  expensive
	 * process and not recommended. Tickets are indexed by Lucene and queries
	 * should be executed against that index.
	 *
	 * @param repository
	 * @param filter
	 *            optional filter to only return matching results
	 * @return a list of tickets
	 */
@Override
public List<TicketModel> getTickets(RepositoryModel repository, TicketFilter filter) {
    List<TicketModel> list = new ArrayList<TicketModel>();
    Repository db = repositoryManager.getRepository(repository.name);
    try {
        // Collect the set of all json files
        File dir = new File(db.getDirectory(), TICKETS_PATH);
        List<File> journals = findAll(dir, JOURNAL);
        // Deserialize each ticket and optionally filter out unwanted tickets
        for (File journal : journals) {
            String json = null;
            try {
                json = new String(FileUtils.readContent(journal), Constants.ENCODING);
            } catch (Exception e) {
                log.error(null, e);
            }
            if (StringUtils.isEmpty(json)) {
                // journal was touched but no changes were written
                continue;
            }
            try {
                // Reconstruct ticketId from the path
                // id/26/326/journal.json
                String path = FileUtils.getRelativePath(dir, journal);
                String tid = path.split("/")[1];
                long ticketId = Long.parseLong(tid);
                List<Change> changes = TicketSerializer.deserializeJournal(json);
                if (ArrayUtils.isEmpty(changes)) {
                    log.warn("Empty journal for {}:{}", repository, journal);
                    continue;
                }
                TicketModel ticket = TicketModel.buildTicket(changes);
                ticket.project = repository.projectPath;
                ticket.repository = repository.name;
                ticket.number = ticketId;
                // add the ticket, conditionally, to the list
                if (filter == null) {
                    list.add(ticket);
                } else {
                    if (filter.accept(ticket)) {
                        list.add(ticket);
                    }
                }
            } catch (Exception e) {
                log.error("failed to deserialize {}/{}\n{}", new Object[] { repository, journal, e.getMessage() });
                log.error(null, e);
            }
        }
        // sort the tickets by creation
        Collections.sort(list);
        return list;
    } finally {
        db.close();
    }
}
Also used : Repository(org.eclipse.jgit.lib.Repository) ArrayList(java.util.ArrayList) TicketModel(com.gitblit.models.TicketModel) Change(com.gitblit.models.TicketModel.Change) File(java.io.File) IOException(java.io.IOException)

Example 40 with TicketModel

use of com.gitblit.models.TicketModel in project gitblit by gitblit.

the class FileTicketService method getTicketImpl.

/**
	 * Retrieves the ticket from the repository.
	 *
	 * @param repository
	 * @param ticketId
	 * @return a ticket, if it exists, otherwise null
	 */
@Override
protected TicketModel getTicketImpl(RepositoryModel repository, long ticketId) {
    Repository db = repositoryManager.getRepository(repository.name);
    try {
        List<Change> changes = getJournal(db, ticketId);
        if (ArrayUtils.isEmpty(changes)) {
            log.warn("Empty journal for {}:{}", repository, ticketId);
            return null;
        }
        TicketModel ticket = TicketModel.buildTicket(changes);
        if (ticket != null) {
            ticket.project = repository.projectPath;
            ticket.repository = repository.name;
            ticket.number = ticketId;
        }
        return ticket;
    } finally {
        db.close();
    }
}
Also used : Repository(org.eclipse.jgit.lib.Repository) TicketModel(com.gitblit.models.TicketModel) Change(com.gitblit.models.TicketModel.Change)

Aggregations

TicketModel (com.gitblit.models.TicketModel)62 Change (com.gitblit.models.TicketModel.Change)32 Test (org.junit.Test)28 RevCommit (org.eclipse.jgit.revwalk.RevCommit)21 IOException (java.io.IOException)18 Reference (com.gitblit.models.TicketModel.Reference)16 Repository (org.eclipse.jgit.lib.Repository)12 Patchset (com.gitblit.models.TicketModel.Patchset)8 RepositoryModel (com.gitblit.models.RepositoryModel)7 TicketLink (com.gitblit.models.TicketModel.TicketLink)7 Ref (org.eclipse.jgit.lib.Ref)5 UserModel (com.gitblit.models.UserModel)4 ArrayList (java.util.ArrayList)4 ReceiveCommand (org.eclipse.jgit.transport.ReceiveCommand)4 Attachment (com.gitblit.models.TicketModel.Attachment)3 ParseException (java.text.ParseException)3 RevWalk (org.eclipse.jgit.revwalk.RevWalk)3 TicketHook (com.gitblit.extensions.TicketHook)2 PathChangeModel (com.gitblit.models.PathModel.PathChangeModel)2 BranchTicketService (com.gitblit.tickets.BranchTicketService)2