コード例 #1
0
        /// <summary>
        ///     Initializes the <see cref="SynergyConnectionInfo.Delimiter"/> and
        ///     <see cref="SynergyProjectInfo.ObjectName"/>, and
        ///     the <see cref="SynergyProjectInfo.WorkAreaPath"/> fields.
        /// </summary>
        /// <exception cref="CruiseControlException">
        ///     If the <see cref="SynergyCommandBuilder.GetDelimiter"/> or
        ///     <see cref="SynergyCommandBuilder.GetDcmDelimiter"/> commands fail
        ///     to return a stdout stream.  Also called if the work area is an invalid
        ///     path, which can happen if the project has not been synchronized on
        ///     the client machine.
        /// </exception>
        private void Initialize()
        {
            ProcessInfo   info;
            ProcessResult result;
            string        temp;

            try
            {
                // set the delimiter for the database
                info = SynergyCommandBuilder.GetDelimiter(connection);
                info.EnvironmentVariables[SessionToken] = connection.SessionId;
                result = Execute(info);
                temp   = result.StandardOutput;
                if (temp.Length == 0)
                {
                    throw(new CruiseControlException("Failed to read the CM Synergy delimiter"));
                }
                connection.Delimiter = temp[0];
            }
            catch (Exception inner)
            {
                throw(new CruiseControlException("Failed to read the CM Synergy database delimiter", inner));
            }

            try
            {
                // set the project spec
                info = SynergyCommandBuilder.GetProjectFullName(connection, project);
                info.EnvironmentVariables[SessionToken] = connection.SessionId;
                result             = Execute(info);
                temp               = result.StandardOutput.Trim();
                project.ObjectName = temp;
            }
            catch (Exception inner)
            {
                temp = String.Concat(@"CM Synergy Project """, project.ProjectSpecification, @""" not found");
                throw(new CruiseControlException(temp, inner));
            }

            try
            {
                // read the project work area path, to use for the working directory for ccm commands
                info = SynergyCommandBuilder.GetWorkArea(connection, project);
                info.EnvironmentVariables[SessionToken] = connection.SessionId;
                result = Execute(info);

                project.WorkAreaPath = Path.GetFullPath(result.StandardOutput.Trim());
                if (!Directory.Exists(project.WorkAreaPath))
                {
                    throw(new CruiseControlException(String.Concat("CM Synergy work area '", result.StandardOutput.Trim(), "' not found.")));
                }

                Log.Info(String.Concat(project.ProjectSpecification, " work area is '", project.WorkAreaPath, "'"));
            }
            catch (Exception inner)
            {
                temp = String.Concat(@"CM Synergy Work Area for Project """, project.ProjectSpecification, @""" could not be determined.");
                throw(new CruiseControlException(temp, inner));
            }
        }
コード例 #2
0
        /// <summary>
        ///     If enabled, discards changes to specified work area paths.
        /// </summary>
        /// <remarks>
        ///     Supports both file and directory paths.  Useful if you build process
        ///     emits artifacts under source control.  Changes to controlled files can cause
        ///     reconfigure commands to fail.
        /// </remarks>
        private void Reconcile()
        {
            // force a connection to be established, if it hasn't already
            // need in case of a forced build
            command.Execute(SynergyCommandBuilder.Heartbeat(connection));

            if (null != project.ReconcilePaths)
            {
                string fullPath;
                foreach (string path in project.ReconcilePaths)
                {
                    // normalize the path
                    if (!Path.IsPathRooted(path))
                    {
                        fullPath = Path.Combine(project.WorkAreaPath, path);
                    }
                    else
                    {
                        fullPath = path;
                    }

                    fullPath = Path.GetFullPath(fullPath);
                    Log.Info(String.Concat("Reconciling work area path '", path, "'"));

                    // sync the work area to discard work area changes
                    command.Execute(SynergyCommandBuilder.Reconcile(connection, project, path));
                }
            }
        }
コード例 #3
0
        /// <summary>
        ///     Stops the Synergy session, if one was previously opened.
        /// </summary>
        private void Close()
        {
            ProcessInfo info;

            if (isOpen)
            {
                info = SynergyCommandBuilder.Stop(connection);

                // set the session id for ccm.exe to use
                info.EnvironmentVariables[SessionToken] = connection.SessionId;
                //Make sure the thread has a name so the ProcessExecuter will not crash
                if (string.IsNullOrEmpty(Thread.CurrentThread.Name))
                {
                    Thread.CurrentThread.Name = connection.SessionId;
                }

                /* This should be a fire-and-forget call.
                 * We don't want an exception thrown if the session cannot be stopped. */
                /* don't call this.Execute(), as it will cause an infinite loop
                 * once this.ValidateSession is called. */
                executor.Execute(info);

                // reset the CCM_ADDR value and delimiter fields
                connection.Reset();
            }

            // reset the open flag
            isOpen = false;
        }
コード例 #4
0
        /// <summary>
        ///     Gets the date of the project's last reconfigure time to ensure consistency.
        /// </summary>
        /// <exception cref="CruiseControlException">
        ///     Thrown if the last reconfigure time cannot be read or parsed successfully.
        /// </exception>
        /// <returns></returns>
        private DateTime GetReconfigureTime()
        {
            // setup the project to reconfigure using the default template
            ProcessResult result = command.Execute(SynergyCommandBuilder.GetLastReconfigureTime(connection, project));

            try
            {
                return(DateTime.Parse(result.StandardOutput.Trim(), CultureInfo.InvariantCulture));
            }
            catch (Exception inner)
            {
                throw(new CruiseControlException("Failed to read the project's last reconfigure time.", inner));
            }
        }
コード例 #5
0
        /// <summary>
        ///     Performs a CM Synergy "Reconfigure"/"Update Members" for a forced build.
        /// </summary>
        /// <remarks>
        ///     <see cref="GetModifications"/> will also reconfigure when modifications are detected
        ///     which explains why this method is a no-op unless we have a forced build.
        /// </remarks>
        /// <param name="integration">Not used.</param>
        /// <url>element://model:project::CCNet.Synergy.Plugin/design:view:::ax60xur0dt7rg6h_v</url>
        public void GetSource(IIntegrationResult integration)
        {
            // reconcile any work area paths specificed by the config file
            Reconcile();

            // reconfigure the project
            ProcessInfo info = SynergyCommandBuilder.Reconfigure(connection, project);

            command.Execute(info);

            /* UNDO -- refactored out - no longer need to count the number of objects replaced
             * int objectsReplaced = SynergyParser.GetReconfigureCount(processResult.StandardOutput); */

            // update the timestamp of the reconfigure
            project.LastReconfigureTime = GetReconfigureTime();
        }
コード例 #6
0
        /// <summary>
        ///    Guarantees that a Synergy session is open, alive, and usable.
        /// </summary>
        private void ValidateSession()
        {
            // check that we have a CCM Address
            bool isValid = (null != connection.SessionId && connection.SessionId.Length > 0);

            // if the connection is open, execute the heartbeat command
            // to ensure that the server connection was not lost
            if (isOpen && isValid)
            {
                ProcessInfo info = SynergyCommandBuilder.Heartbeat(connection);
                if (null != project && null != project.WorkAreaPath && project.WorkAreaPath.Length > 0)
                {
                    info = new ProcessInfo(info.FileName, info.Arguments, project.WorkAreaPath);
                }

                // set the session id for ccm.exe to use
                if (null != connection && null != connection.SessionId && connection.SessionId.Length > 0)
                {
                    info.EnvironmentVariables[SessionToken] = connection.SessionId;
                }

                /* don't call this.Execute(), as it will cause an infinite loop
                 * once this.ValidateSession is called. */
                ProcessResult result = executor.Execute(info);

                // reset the valid flag, if ccm status does not report the session token
                isValid = IsSessionAlive(result.StandardOutput, connection.SessionId, connection.Database);

                if (!isValid)
                {
                    // Call the close method, since there's likely a problem with the client/server
                    // connection.  This call will not throw an exception even if it fails.
                    Close();
                }
            }

            // (re-)open the session if one could not be found, or if it was killed
            if (!isValid)
            {
                // Now try to re-establish a connection
                Open();
            }
        }
コード例 #7
0
        private Modification[] GetModifications(DateTime from)
        {
            Modification[] modifications = new Modification[0];

            if (project.TemplateEnabled)
            {
                // setup the project to reconfigure using the default template
                command.Execute(SynergyCommandBuilder.UseReconfigureTemplate(connection, project));
            }

            // refresh the query based folders in the reconfigure properties
            command.Execute(SynergyCommandBuilder.UpdateReconfigureProperites(connection, project));

            // this may fail, if a build was forced, and no changes were found
            ProcessResult result = command.Execute(SynergyCommandBuilder.GetNewTasks(connection, project, from), false);

            if (!result.Failed)
            {
                // cache the output of the task/comment query
                string comments = result.StandardOutput;

                // populate the selection set with the objects associated with the detected tasks
                result = command.Execute(SynergyCommandBuilder.GetTaskObjects(connection, project), false);

                if (!result.Failed)
                {
                    // Get the path information for each object associated with the tasks
                    result = command.Execute(SynergyCommandBuilder.GetObjectPaths(connection, project), false);
                    if (!result.Failed)
                    {
                        modifications = parser.Parse(comments, result.StandardOutput, from);

                        if (null != urlBuilder)
                        {
                            urlBuilder.SetupModification(modifications);
                        }
                    }
                }
            }

            FillIssueUrl(modifications);
            return(modifications);
        }
コード例 #8
0
        /// <summary>
        ///     Stops the Synergy session, if one was previously opened.
        /// </summary>
        private void Close()
        {
            ProcessInfo info;

            if (isOpen)
            {
                info = SynergyCommandBuilder.Stop(connection);

                /* This should be a fire-and-forget call.
                 * We don't want an exception thrown if the session cannot be stopped. */
                /* don't call this.Execute(), as it will cause an infinite loop
                 * once this.ValidateSession is called. */
                executor.Execute(info);

                // reset the CCM_ADDR value and delimiter fields
                connection.Reset();
            }

            // reset the open flag
            isOpen = false;
        }
コード例 #9
0
        /// <summary>
        ///     Adds tasks to a shared task folder, if configured, and creates a baseline
        ///     if requested by the configuration.
        /// </summary>
        /// <remarks>
        ///     <note type="implementnotes">
        ///         This method makes use of CM Synergy selection commands, in order to pipe the
        ///         results of one query to another command.  If the CM Synergy session is lost
        ///         during the course of execution in this method, the selection set is also lost.
        ///         An exception will be thrown when the next CM Synergy command is executed,
        ///         because the selection set is empty. This should be a very rare case, and the
        ///         performance gains of piping resultsets are worthwhile.
        ///     </note>
        /// </remarks>
        /// <exception cref="CruiseControlException">
        ///     Thrown if an external process has reconfigured the project since
        ///     <see cref="GetModifications(ThoughtWorks.CruiseControl.Core.IIntegrationResult, ThoughtWorks.CruiseControl.Core.IIntegrationResult)"/> was called.
        /// </exception>
        /// <param name="result">Not used.</param>
        /// <url>element://model:project::CCNet.Synergy.Plugin/design:view:::ow43bejw6wm4was_v</url>
        public override void LabelSourceControl(IIntegrationResult result)
        {
            if (result.Succeeded)
            {
                DateTime currentReconfigureTime = GetReconfigureTime();
                if (currentReconfigureTime != project.LastReconfigureTime)
                {
                    string message = String.Format(CultureInfo.CurrentCulture, @"Invalid project state.  Cannot add tasks to shared folder '{0}' because " + @"the integration project '{1}' was internally reconfigured at '{2}' " + @"and externally reconfigured at '{3}'.  Projects cannot be reconfigured " + @"during an integration run.", project.TaskFolder, project.ProjectSpecification, project.LastReconfigureTime, currentReconfigureTime);
                    throw(new CruiseControlException(message));
                }

                /* Populate the query selection set with a list of ALL tasks
                 * not in the manual folder. This includes all tasks for this integration,
                 * and any prior failed integrations.
                 * We find these by passing the the maximum range of dates to GetModifications */
                result.Modifications = GetModifications(DateTime.MinValue);

                // skip this step if a build was forced, and no changes were found
                if (null != result.Modifications && result.Modifications.Length > 0)
                {
                    // comment those tasks with the "label", for both shared folders and baselines
                    command.Execute(SynergyCommandBuilder.AddLabelToTaskComment(connection, project, result));

                    // append tasks to the shared folder, if one was specified
                    if (SynergyProjectInfo.DefaultTaskFolder != project.TaskFolder)
                    {
                        // append those tasks in the selection set to the shared build folder
                        command.Execute(SynergyCommandBuilder.AddTasksToFolder(connection, project, result));
                    }
                }

                // create a baseline, if requested
                if (project.BaseliningEnabled)
                {
                    command.Execute(SynergyCommandBuilder.CreateBaseline(connection, project, result));
                }
            }
        }
コード例 #10
0
        /// <summary>
        ///     Starts a new Synergy session.
        /// </summary>
        /// <exception cref="CruiseControlException">
        ///     Thrown if <see cref="SynergyCommandBuilder.Start"/> fails to write
        ///     a single line containing the CCM_ADDR value.
        /// </exception>
        private void Open()
        {
            ProcessInfo   info;
            ProcessResult result;
            int           originalTimeout;
            string        temp;
            string        message;

            if (!isOpen)
            {
                info = SynergyCommandBuilder.Start(connection, project);

                /* Serialize the calls to <c>ccm.exe start</c>, as the CM Synergy
                 * client does not properly support concurrent calls to this command for the
                 * same CM Synergy username.
                 *
                 * TODO: Should we optimize this to have shared locks only for the same CM Synergy
                 *       username?  It's doutbful that large projects would have different credentials,
                 *       so there may be little gain for this functionality */
                Log.Debug("Queued for critical section to open CM Synergy session; blocking until lock is acquired.");
                lock (PadLock)
                {
                    Log.Debug("Acquired lock to open a session");

                    /* Don't call this.Execute(), as it will cause an infinite loop
                     * once this.ValidateSession is called. */
                    result = executor.Execute(info);
                    Log.Debug("Releasing lock to open a session");
                }

                if (result.TimedOut)
                {
                    message = String.Format(@"Synergy connection timed out after {0} seconds.", connection.Timeout);
                    throw(new CruiseControlException(message));
                }

                // suspend the thread if the database is protected, and the sleep option is enabled
                if (result.Failed)
                {
                    if (connection.PollingEnabled)
                    {
                        if (IsDatabaseProtected(result.StandardError, connection.Host, connection.Database))
                        {
                            // sleep the thread for timeout until the database is unprotected
                            Log.Warning(String.Format("Database {0} on Host {1} Is Protected.  Waiting 60 seconds to reconnect.", connection.Host, connection.Database));
                            Thread.Sleep(new TimeSpan(0, 1, 0));

                            // save the original timeout
                            originalTimeout = connection.Timeout;

                            // decrement the time to wait for the backup to complete, so that we don't wait forever
                            connection.Timeout -= 60;

                            //recursively call the open function
                            Open();

                            // revent the timeout prior to recursion
                            connection.Timeout = originalTimeout;

                            // exit the call stack, breaking out of the recursive loop
                            return;
                        }
                    }
                }

                temp = result.StandardOutput;

                if (null != temp && temp.Length > 0)
                {
                    connection.SessionId = temp.Trim();
                    Log.Info(String.Concat("CCM_ADDR set to '", connection.SessionId, "'"));
                }
                else
                {
                    throw(new CruiseControlException("CM Synergy logon failed"));
                }

                // read the delimiter fields
                Initialize();

                // update the release setting of the project and all subprojects
                info = SynergyCommandBuilder.GetSubProjects(connection, project);
                info.EnvironmentVariables[SessionToken] = connection.SessionId;
                executor.Execute(info);
                info = SynergyCommandBuilder.SetProjectRelease(connection, project);
                info.EnvironmentVariables[SessionToken] = connection.SessionId;
                executor.Execute(info);

                isOpen = true;
            }
        }