Welcome to the Inedo Forums! Check out the Forums Guide for help getting started.

If you are experiencing any issues with the forum software, please visit the Contact Form on our website and let us know!

Buildmaster SDK - ReleaseNumber



  • Hey,

    Just trying to upgrade the Inedo.Buildmaster.SDK to v6.0.0. Running into an issue where:

    public override Task ExecuteAsync(IOperationExecutionContext context)
    {
         return Task.Run(async () => {
             var releaseNumber = $"{context.ReleaseNumber}.{context.BuildNumber}";
         });
    }
    

    is now invalid because context doesn't contain ReleaseNumber or BuildNumber.

    How can I get the current release and build numbers in the new SDK version?

    Thanks!
    Chris

    Product: BuildMaster
    Version: 6.0.6



  • Hi Chris,

    That appears to have been an oversight in the Inedo SDK. However, you should be able to just cast context to Inedo.BuildMaster.Extensibility.IGenericBuildMasterContext, which does have a ReleaseNumber and BuildNumber property.

    This is safe to do; it's how we have the $ReleaseNumber function implemented internally.

    Hope this helps!



  • Thanks Dean, worked like a charm.



  • Hi Dean,

    Turns out that built it, but when I cast the IExecutionContext to an IGenericBuildmasterContext it just returns null when run inside Buildmaster.

    Any ideas?

    Cheers,
    Chris



  • Are you able to post the full code, or submit the code as a support ticket if you don't want it public? There must be something else happening because that should definitely work.



  • No worries Todd - here's the code:

    Line 62 logs "Package Context is null? True".

    Line 133 is where the exception gets thrown: NullReferenceException

    Inedo SDK is v1.0.5 (which at the time of writing is the latest stable version)

    using Inedo;
    using Inedo.BuildMaster.Data;
    using Inedo.BuildMaster.Extensibility;
    using Inedo.Diagnostics;
    using Inedo.Documentation;
    using Inedo.ExecutionEngine.Executer;
    using Inedo.Extensibility;
    using Inedo.Extensibility.Operations;
    using LibGit2Sharp;
    using Custom.BuildMaster.Interfaces;
    using System;
    using System.ComponentModel;
    using System.IO;
    using System.Threading.Tasks;
    
    namespace Custom.BuildMaster.Actions.Git
    {
        [DisplayName("Git-Clone")]
        [Description("Clone's a repository")]
        [ScriptAlias("Git-Clone")]
        [ScriptNamespace("CUSTOM")]
        [Serializable]
        public class GitClone : GitOperation
        {
            [Required]
            [ScriptAlias("Branch")]
            [DisplayName("Branch to Clone")]
            [PlaceholderText("e.g. master")]
            public string Branch { get; set; }
    
            [ScriptAlias("Commit")]
            [DisplayName("Commit Hash")]
            [PlaceholderText("e.g. 3917402e798a42fbdde99a66b8cac5d61a720b8f or leave blank for latest")]
            public string Commit { get; set; }
    
            [Required]
            [ScriptAlias("CloneTo")]
            [DisplayName("Clone to")]
            [PlaceholderText("e.g. $WorkingDirectory")]
            public string CloneTo { get; set; }
    
            [ScriptAlias("SaveToVariable")]
            [DisplayName("Save Commit Hash to Variable")]
            [PlaceholderText("VariableName (optional)")]
            public string VariableName { get; set; }
    
            protected override ExtendedRichDescription GetDescription(IOperationConfiguration config)
            {
                return new ExtendedRichDescription(
                    new RichDescription(
                        $"Clone {config[nameof(this.RepositoryName)]} to {config[nameof(this.CloneTo)]}"
                    )
                );
            }
    
            public override Task ExecuteAsync(IOperationExecutionContext context)
            {
                return Task.Run(async () =>
                {
                    // Turn this into the buildmaster context to get package variables out
                    var packageContext = context as IGenericBuildMasterContext;                
                    Log(new SimpleLogMessage(MessageLevel.Debug, $"Package Context is null? {packageContext == null}", "Git", "", context));  // THIS LINE LOGS "Package Context is null? True"
    
                    // Clone Repo to directory
                    CloneOptions co = new CloneOptions();
    
                    co.CredentialsProvider = (_url, _user, _cred) => new LibGit2Sharp.SecureUsernamePasswordCredentials { Username = Username, Password = Password };
    
                    var path = context.ResolvePath(CloneTo);
    
                    path = Repository.Clone(Path.Combine(BaseUrl, RepositoryName), path, co);
    
                    // Get Commit hash
                    var repo = new Repository(path);
                    var commitHash = repo.Head.Tip.Sha;
    
                    // Get local + remote branch names
                    var localBranchName = "refs/heads/" + Branch;
                    var remoteBranchName = "refs/remotes/origin/" + Branch;
    
                    Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, $"Local Branch set to {localBranchName}", "Git", "", context));
                    Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, $"Remote Branch set to {remoteBranchName}", "Git", "", context));
    
                    // Check the remote branch exists
                    var trackingBranch = repo.Branches[remoteBranchName];
                    if (trackingBranch == null)
                    {
                        Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Error, $"Branch {Branch} does not exist on the remote.", "Git", "", context));
                        throw new Exception($"Branch {Branch} does not exist on remote");
                    }
    
                    // Checkout branch
                    Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, $"Creating Branch {Branch} Locally", "Git", "", context));
                    Branch localBranch;
                    try
                    {
                        localBranch = repo.CreateBranch(Branch, trackingBranch.Tip);
                    }catch
                    {
                        // Branch already exists - set it up
                        localBranch = repo.Branches[localBranchName];
                    }
    
                    Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, "Tracking remote branch", "Git", "", context));
                    repo.Branches.Update(localBranch, b => b.TrackedBranch = trackingBranch.CanonicalName);
    
                    Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, "Checking out branch", "Git", "", context));
                    Commands.Checkout(repo, Branch);
                    Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, $"Cloned branch {Branch} Successfully", "Git", "", context));
    
                    // If hash is specified, checkout that
                    if (!string.IsNullOrWhiteSpace(Commit))
                    {
                        Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, $"Commit Hash set. Attempting to checkout commit hash {Commit}", "Git", "", context));
                        repo.Reset(ResetMode.Hard, Commit);                    
                    }
    
                    // Make sure the commit hash is set correctly
                    commitHash = repo.Head.Tip.Sha;
    
                    Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Information, $"Cloned Successfully: Hash is {commitHash}", "Git", "", context));
    
                    // Set commit hash 
                    if (String.IsNullOrWhiteSpace(VariableName))
                    {
                        Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Information, "No Commit Hash Variable Specified. Not setting.", "Git", "", context));
                    }
                    else
                    {
    
                        using (var db = new DB.Context())
                        {
                            var build = await db.Builds_GetBuildAsync(packageContext.ApplicationId, packageContext.ReleaseNumber, packageContext.BuildNumber); // THIS LINE FAILS with NullReferenceException
                            if (build == null)
                                throw new ExecutionFailureException($"Package {packageContext.BuildNumber} was not found for release {packageContext.ReleaseNumber}.");
    
                            db.Variables_CreateOrUpdatePackageVariable(
                                Build_Id: build.Build_Id,
                                Variable_Name: VariableName,
                                ValueType_Code: Domains.VariableValueType.Scalar,
                                Variable_Value: InedoLib.UTF8Encoding.GetBytes(commitHash),
                                Sensitive_Indicator: false,
                                EvaluateVariables_Indicator: false
                            );
    
                            Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Information, $"Assigned commit hash {commitHash} to variable {VariableName}", "Git", "", context));
                        }
                    }
    
                    //Delete the .git Directory
                    repo.Dispose();
                    Helpers.DirectoryHelper.DeleteReadOnlyDirectory(path);
                    Helpers.DirectoryHelper.DeleteReadOnlyFile(context.ResolvePath(".gitignore"));
                    Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, "Removed .git directory and .gitignore", "Git", "", context));
    
                    // Write hash
                    return new object();
                }); 
            }
        }
    }


  • Try the following code; it removes the need for IGenericBuildMasterContext altogether and uses an async method to replace the Task.Run call:


        public override async Task ExecuteAsync(IOperationExecutionContext context)
        {
            // Clone Repo to directory
            CloneOptions co = new CloneOptions();
    
            co.CredentialsProvider = (_url, _user, _cred) => new LibGit2Sharp.SecureUsernamePasswordCredentials { Username = Username, Password = Password };
    
            var path = context.ResolvePath(CloneTo);
    
            path = Repository.Clone(Path.Combine(BaseUrl, RepositoryName), path, co);
    
            // Get Commit hash
            var repo = new Repository(path);
            var commitHash = repo.Head.Tip.Sha;
    
            // Get local + remote branch names
            var localBranchName = "refs/heads/" + Branch;
            var remoteBranchName = "refs/remotes/origin/" + Branch;
    
            Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, $"Local Branch set to {localBranchName}", "Git", "", context));
            Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, $"Remote Branch set to {remoteBranchName}", "Git", "", context));
    
            // Check the remote branch exists
            var trackingBranch = repo.Branches[remoteBranchName];
            if (trackingBranch == null)
            {
                Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Error, $"Branch {Branch} does not exist on the remote.", "Git", "", context));
                throw new Exception($"Branch {Branch} does not exist on remote");
            }
    
            // Checkout branch
            Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, $"Creating Branch {Branch} Locally", "Git", "", context));
            Branch localBranch;
            try
            {
                localBranch = repo.CreateBranch(Branch, trackingBranch.Tip);
            }
            catch
            {
                // Branch already exists - set it up
                localBranch = repo.Branches[localBranchName];
            }
    
            Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, "Tracking remote branch", "Git", "", context));
            repo.Branches.Update(localBranch, b => b.TrackedBranch = trackingBranch.CanonicalName);
    
            Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, "Checking out branch", "Git", "", context));
            Commands.Checkout(repo, Branch);
            Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, $"Cloned branch {Branch} Successfully", "Git", "", context));
    
            // If hash is specified, checkout that
            if (!string.IsNullOrWhiteSpace(Commit))
            {
                Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, $"Commit Hash set. Attempting to checkout commit hash {Commit}", "Git", "", context));
                repo.Reset(ResetMode.Hard, Commit);
            }
    
            // Make sure the commit hash is set correctly
            commitHash = repo.Head.Tip.Sha;
    
            Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Information, $"Cloned Successfully: Hash is {commitHash}", "Git", "", context));
    
            // Set commit hash 
            if (String.IsNullOrWhiteSpace(VariableName))
            {
                Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Information, "No Commit Hash Variable Specified. Not setting.", "Git", "", context));
            }
            else
            {
                using (var db = new DB.Context())
                {
                    var exec = await db.Builds_GetExecutionAsync(context.ExecutionId);
    
                    await db.Variables_CreateOrUpdatePackageVariableAsync(
                        Build_Id: exec.Build_Id,
                        Variable_Name: VariableName,
                        ValueType_Code: Domains.VariableValueType.Scalar,
                        Variable_Value: InedoLib.UTF8Encoding.GetBytes(commitHash),
                        Sensitive_Indicator: false,
                        EvaluateVariables_Indicator: false
                    );
    
                    Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Information, $"Assigned commit hash {commitHash} to variable {VariableName}", "Git", "", context));
                }
            }
    
            //Delete the .git Directory
            repo.Dispose();
            Helpers.DirectoryHelper.DeleteReadOnlyDirectory(path);
            Helpers.DirectoryHelper.DeleteReadOnlyFile(context.ResolvePath(".gitignore"));
            Log(new SimpleLogMessage(Inedo.Diagnostics.MessageLevel.Debug, "Removed .git directory and .gitignore", "Git", "", context));              
        }
    

    However, I'm guessing this was written a while ago before the built-in Git operations handled this? We use the Git operations to perform this same functionality by combining two operations:

    # Gets source from the BuildMaster repository and creates a release package variable named GitCommit
    Git::Git-GetSource
    (
        Credentials: GitLab,
        RepositoryUrl: https://gitlab.com/inedo/BuildMaster.git,
        DiskPath: ~\Src,
        RecurseSubmodules: true,
        Branch: $Branch,
        CommitHash => $commit
    );
    
    Set-ReleaseVariable GitCommit
    (
        Value: $commit,
        Package: $PackageNumber
    );


  • Hi Todd,

    Thanks for the such quick replies. I've implemented that in the code, but will need to wait for a bit to actually test it.

    Is there a way of cloning a particular commit using the built-in extension (as above) or can you only clone branches? I think that's where this extension initially arose.



  • You can actually put a full commit ID in the Tag field (which actually supports any refname)... I'll have someone put a note in to change that in the operation.



Inedo Website HomeSupport HomeCode of ConductForums GuideDocumentation