<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mehmet Seçkin</title>
    <description>The latest articles on DEV Community by Mehmet Seçkin (@mehmetseckin).</description>
    <link>https://dev.to/mehmetseckin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F92957%2F34d8f4c9-6441-46e8-97ff-d94207019e7e.jpeg</url>
      <title>DEV Community: Mehmet Seçkin</title>
      <link>https://dev.to/mehmetseckin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mehmetseckin"/>
    <language>en</language>
    <item>
      <title>Continuously Delivering a PCF Control</title>
      <dc:creator>Mehmet Seçkin</dc:creator>
      <pubDate>Fri, 21 Feb 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/mehmetseckin/continuously-delivering-a-pcf-control-371c</link>
      <guid>https://dev.to/mehmetseckin/continuously-delivering-a-pcf-control-371c</guid>
      <description>&lt;p&gt;There are lots of things that can be possible with the mighty PowerApps Component Framework. The component development experience is great thanks to the scripting provided by the framework, however once you’re happy with your component, “shipping it” may not be so easy. You still have to deal with the produced solution archive (.zip) file. Moreover, the properties of this solution file is in an XML file in your repository, for example, you’ll have to increment the version number for the solution that will be output by your build process manually. This is fine if it’s a one-off operation, but it can be a little daunting when you have to repeat it over and over during your dev/test loop, or if you want to implement continuous delivery.&lt;/p&gt;

&lt;h2&gt;
  
  
  An example scenario
&lt;/h2&gt;

&lt;p&gt;I have recently developed a PCF control to learn the framework, and decided to implement continuous integration and delivery. To achieve this, I decided to use Azure Pipelines and release the control as a managed solution to GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kMgsAWay--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mehmetseckin.com/images/pcf-pipelines-github.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kMgsAWay--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mehmetseckin.com/images/pcf-pipelines-github.png" alt="CI/CD Overview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s start with a CI pipeline that builds our new control:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stages:
- stage: 'build'
  jobs:
  - job: 'build_job'
    pool:
      vmAgent: 'windows-latest'
    steps:
    - task: VSBuild@1
      displayName: "Build"
      inputs:
        solution: 'Solutions\Solutions.cdsproj'
        msbuildArgs: '/t:build /restore'

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Well, that was easy. However, our solution is getting built in &lt;code&gt;Debug&lt;/code&gt; mode, and this produces an &lt;code&gt;unmanaged&lt;/code&gt; solution by default. You can manually override this setting by specifying a property in the &lt;code&gt;Solutions.cdsproj&lt;/code&gt; file. (Hint: The property is included as a comment in the generated project file).&lt;/p&gt;

&lt;p&gt;In this scenario, we will just tell the pipeline to build in &lt;code&gt;Release&lt;/code&gt; mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ...
        msbuildArgs: '/t:build /restore /p:configuration=Release' # build in Release mode to get managed solution

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Great, now we have our managed solution. Let’s give our build a name to provide unique version numbers to each build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variables:
  version: '1.0.0'

name: $(version).$(build.buildId)

# stages: ...

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now each build has its own unique version number, but our solution version never changes. To update the solution version, we will use a simple PowerShell step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ...
    steps:
    - powershell: |
        echo "Updating solution version: $($env:BUILD_NUMBER)"
        $solution = [xml](gc "$($env:SOLUTION_XML_PATH)")
        $solution.ImportExportXml.SolutionManifest.Version = "$($env:BUILD_NUMBER)";
        $solution.Save("$($env:SOLUTION_XML_PATH)")
      displayName: 'Update solution version'
      env:
        BUILD_NUMBER: $(build.buildNumber) # a.k.a. the 'name', e.g. 1.0.0.5523
        SOLUTION_XML_PATH: 'Solutions\Other\Solution.xml'
    # - task: VSBuild@1 ...

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, every build produces a managed solution with a unique version number. It’s time to tag the source in GitHub, and publish our managed solution to GitHub as a release.&lt;/p&gt;

&lt;p&gt;To do this, we will first need to add a checkout step with &lt;code&gt;persistCredentials&lt;/code&gt; flag set to &lt;code&gt;true&lt;/code&gt; at the beginning of our build job, and a PowerShell script to create and push a tag to our repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    steps:
    - checkout: self
      persistCredentials: true

    # ... build tasks

    - powershell: |
        echo "Tagging Build: $($env:BUILD_NUMBER)"
        git tag $env:BUILD_NUMBER
        git push origin $env:BUILD_NUMBER
      env:
        BUILD_NUMBER: $(build.buildNumber) # a.k.a. the 'name', e.g. 1.0.0.5523
        GIT_REDIRECT_STDERR: 2&amp;gt;&amp;amp;1 # This is needed because git writes warnings to error stream and this fails the task

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now we can use the GitHub Release task to release our solution to GitHub. The &lt;code&gt;githubConnection&lt;/code&gt; parameter accepts a Service Connection name which can be configured on Azure Pipelines in &lt;code&gt;Project Settings &amp;gt; Service Connections&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    - task: GitHubRelease@1
      inputs:
        gitHubConnection: 'azure-pipelines-github-connection'
        repositoryName: '$(Build.Repository.Name)'
        action: 'create'
        target: '$(Build.SourceVersion)'
        tagSource: 'gitTag'
        assets: Solutions\bin\Release\*.zip

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our final pipeline definition looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;variables:
  version: '1.0.0'

name: $(version).$(build.buildId)

stages:
- stage: 'build'
  jobs:
  - job: 'build_job'
    pool:
      vmAgent: 'windows-latest'
    steps:
    - checkout: self
      persistCredentials: true
    - powershell: |
        echo "Updating solution version: $($env:BUILD_NUMBER)"
        $solution = [xml](gc "$($env:SOLUTION_XML_PATH)")
        $solution.ImportExportXml.SolutionManifest.Version = "$($env:BUILD_NUMBER)";
        $solution.Save("$($env:SOLUTION_XML_PATH)")
      displayName: 'Update solution version'
      env:
        BUILD_NUMBER: $(build.buildNumber) # a.k.a. the 'name', e.g. 1.0.0.5523
        SOLUTION_XML_PATH: 'Solutions\Other\Solution.xml'
    - task: VSBuild@1
      displayName: "Build"
      inputs:
        solution: 'Solutions\Solutions.cdsproj'
        msbuildArgs: '/t:build /restore'
    - powershell: |
        echo "Tagging Build: $($env:BUILD_NUMBER)"
        git tag $env:BUILD_NUMBER
        git push origin $env:BUILD_NUMBER
      env:
        BUILD_NUMBER: $(build.buildNumber) # a.k.a. the 'name', e.g. 1.0.0.5523
        GIT_REDIRECT_STDERR: 2&amp;gt;&amp;amp;1 # This is needed because git writes warnings to error stream and this fails the task
    - task: GitHubRelease@1
      inputs:
        gitHubConnection: 'azure-pipelines-github-connection'
        repositoryName: '$(Build.Repository.Name)'
        action: 'create'
        target: '$(Build.SourceVersion)'
        tagSource: 'gitTag'
        assets: Solutions\bin\Release\*.zip

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Hope this helps!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My First PCF Control: TimeZoneDisplay</title>
      <dc:creator>Mehmet Seçkin</dc:creator>
      <pubDate>Fri, 21 Feb 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/mehmetseckin/my-first-pcf-control-timezonedisplay-3bne</link>
      <guid>https://dev.to/mehmetseckin/my-first-pcf-control-timezonedisplay-3bne</guid>
      <description>&lt;p&gt;PowerApps Component Framework is a new way to build helpful, reusable components that provide better user experiences in an efficient and maintainable way. I have been looking for a PCF control idea to learn and explore this new framework and its capabilities.&lt;/p&gt;

&lt;p&gt;Recently, I have missed a series of international webinars due to my timezone miscalculations, and decided to address this with a PCF control. This is how the Time Zone Display was born.&lt;/p&gt;

&lt;p&gt;Time Zone Display is a simple control that allows you to convert a date or datetime field to any given time zone. It’s built using &lt;code&gt;React.js&lt;/code&gt; and the Office UI Fabric components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8bsYwBy4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mehmetseckin.com/images/tz_autocomplete.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8bsYwBy4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.mehmetseckin.com/images/tz_autocomplete.png" alt="Time Zone Display"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’d like to check it out, head over to GitHub to download the &lt;a href="https://github.com/mehmetseckin/TimeZoneDisplay/releases/latest"&gt;latest release&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any feedback or would like to improve it, please get involved and create an issue or send a pull request!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A .NET Native Alternative to JSON.NET (a.k.a. Newtonsoft.Json) for Dynamics 365 Plugins</title>
      <dc:creator>Mehmet Seçkin</dc:creator>
      <pubDate>Thu, 23 Jan 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/mehmetseckin/a-net-native-alternative-to-json-net-a-k-a-newtonsoft-json-for-dynamics-365-plugins-3i9a</link>
      <guid>https://dev.to/mehmetseckin/a-net-native-alternative-to-json-net-a-k-a-newtonsoft-json-for-dynamics-365-plugins-3i9a</guid>
      <description>&lt;h1&gt;
  
  
  A .NET Native Alternative to JSON.NET (a.k.a. Newtonsoft.Json) for Dynamics 365 Plugins
&lt;/h1&gt;

&lt;p&gt;23 January 2020, Birmingham, UK&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;JSON.NET (a.k.a Newtonsoft.Json) is a powerful JSON library. But when working with Dynamics 365 plug-ins, it needs to be combined into your plug-in DLL using ILMerge. This works fine, but using ILMerge for plug-in and custom workflow assemblies is &lt;a href="https://cloudblogs.microsoft.com/dynamics365/no-audience/2010/11/09/how-to-reference-assemblies-from-plug-ins/?source=crm"&gt;unsupported&lt;/a&gt; and can jeopardise your support agreement.&lt;/p&gt;

&lt;p&gt;If you don’t want to use ILMerge, or unable to for some reason, you can use &lt;code&gt;DataContractJsonSerializer&lt;/code&gt; for most JSON tasks &lt;a href="https://www.newtonsoft.com/json/help/html/JsonNetVsDotNetSerializers.htm"&gt;JSON.NET&lt;/a&gt; is capable of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Show me the code
&lt;/h2&gt;

&lt;p&gt;Here is an example drop-in replacement class with the same interface as JSON.NET:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;

public static class JsonConvert
{
    public static string Serialize&amp;lt;T&amp;gt;(T obj)
    {
        using (var stream = new MemoryStream())
        {
            GetSerializer&amp;lt;T&amp;gt;().WriteObject(stream, obj);
            return Encoding.UTF8.GetString(stream.ToArray());
        }
    }

    public static T Deserialize&amp;lt;T&amp;gt;(string json)
    {
        using (var stream = new MemoryStream())
        {
            using (var writer = new StreamWriter(stream))
            {
                writer.Write(json);
                writer.Flush();
                stream.Position = 0;
                return (T)GetSerializer&amp;lt;T&amp;gt;().ReadObject(stream);
            }
        }
    }

    private static DataContractJsonSerializer GetSerializer&amp;lt;T&amp;gt;()
    {
        var settings = new DataContractJsonSerializerSettings
        {
            UseSimpleDictionaryFormat = true
        };

        return new DataContractJsonSerializer(typeof(T), settings);
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This can be used in the same way as JSON.NET:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Serialize a Person object into a JSON string
string json = JsonConvert.Serialize(person);

// Deserialize a string into a Person object.
var person = JsonConvert.Deserialize&amp;lt;Person&amp;gt;(json);


using System.Runtime.Serialization;

[DataContract]
public class Person
{
    [DataMember]
    public string FullName { get; set; }

    [DataMember]
    public Dictionary&amp;lt;string, string&amp;gt; Attributes { get; set; }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Hope this saves someone a minute or two!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Perform CRUD Operations using `Xrm.WebApi`</title>
      <dc:creator>Mehmet Seçkin</dc:creator>
      <pubDate>Wed, 18 Sep 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/mehmetseckin/perform-crud-operations-using-xrm-webapi-3a0</link>
      <guid>https://dev.to/mehmetseckin/perform-crud-operations-using-xrm-webapi-3a0</guid>
      <description>&lt;h1&gt;
  
  
  Perform CRUD Operations using &lt;code&gt;Xrm.WebApi&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;I have recently had a challenge where I needed to perform some CRUD operations on a bunch of records, and wondered whether I could use the Client API &lt;code&gt;Xrm.WebApi.online.executeMultiple&lt;/code&gt; to achieve this.&lt;/p&gt;

&lt;p&gt;Here are the examples I’ve come up with, hope this saves someone some time!:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Update: These examples are now in the PowerApps docs, check it out: &lt;a href="https://docs.microsoft.com/en-us/powerapps/developer/model-driven-apps/clientapi/reference/xrm-webapi/online/execute#perform-crud-operations"&gt;Perform CRUD Operations&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Create a record
&lt;/h4&gt;

&lt;p&gt;The following example demonstrates how to perform a Create operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var Sdk = window.Sdk || {};
/**
 * Request to execute a create operation
 */
Sdk.CreateRequest = function (entityName, payload) {
    this.etn = entityName;
    this.payload = payload;
    this.getMetadata = function () {
        return {
            boundParameter: null,
            parameterTypes: {},
            operationType: 2, // This is a CRUD operation. Use '0' for actions and '1' for functions
            operationName: "Create",
        };
    };
};
// Construct a request object from the metadata
var payload = {};
payload["name"] = "Fabrikam Inc.";
var createRequest = new Sdk.CreateRequest("account", payload);
// Use the request object to execute the function
Xrm.WebApi.online.execute(createRequest).then(
    function (result) {
        if (result.ok) {
            console.log("Status: %s %s", result.status, result.statusText);
            // perform other operations as required;
        }
    },
    function (error) {
        console.log(error.message);
        // handle error conditions
    }
);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Retrieve a record
&lt;/h4&gt;

&lt;p&gt;The following example demonstrates how to perform a Retrieve operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var Sdk = window.Sdk || {};
/**
 * Request to execute a retrieve operation
 */
Sdk.RetrieveRequest = function (entityReference, columns) {
    this.entityReference = entityReference;
    this.columns = columns;
    this.getMetadata = function () {
        return {
            boundParameter: null,
            parameterTypes: {},
            operationType: 2, // This is a CRUD operation. Use '0' for actions and '1' for functions
            operationName: "Retrieve",
        };
    };
};
// Construct a request object from the metadata
var retrieveRequest = new Sdk.RetrieveRequest({ etn: "account", id: "87547d08-b9d0-e911-a826-000d3a43d70a" }, ["name"]);
// Use the request object to execute the function
Xrm.WebApi.online.execute(retrieveRequest).then(
    function (result) {
        if (result.ok) {
            console.log("Status: %s %s", result.status, result.statusText);
            // perform other operations as required;
        }
    },
    function (error) {
        console.log(error.message);
        // handle error conditions
    }
);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Update a record
&lt;/h4&gt;

&lt;p&gt;The following example demonstrates how to perform a Update operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var Sdk = window.Sdk || {};
/**
 * Request to execute an update operation
 */
Sdk.UpdateRequest = function (entityName, entityId, payload) {
    this.etn = entityName;
    this.id = entityId;
    this.payload = payload;
    this.getMetadata = function () {
        return {
            boundParameter: null,
            parameterTypes: {},
            operationType: 2, // This is a CRUD operation. Use '0' for actions and '1' for functions
            operationName: "Update",
        };
    };
};
// Construct a request object from the metadata
var payload = {};
payload["name"] = "AdventureWorks Inc.";
var updateRequest = new Sdk.UpdateRequest("account", "87547d08-b9d0-e911-a826-000d3a43d70a", payload);
// Use the request object to execute the function
Xrm.WebApi.online.execute(updateRequest).then(
    function (result) {
        if (result.ok) {
            console.log("Status: %s %s", result.status, result.statusText);
            // perform other operations as required;
        }
    },
    function (error) {
        console.log(error.message);
        // handle error conditions
    }
);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Delete a record
&lt;/h4&gt;

&lt;p&gt;The following example demonstrates how to perform a Delete operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var Sdk = window.Sdk || {};
/**
 * Request to execute a delete operation
 */
Sdk.DeleteRequest = function (entityReference) {
    this.entityReference = entityReference;
    this.getMetadata = function () {
        return {
            boundParameter: null,
            parameterTypes: {},
            operationType: 2, // This is a CRUD operation. Use '0' for actions and '1' for functions
            operationName: "Delete",
        };
    };
};
// Construct a request object from the metadata
var deleteRequest = new Sdk.DeleteRequest({ entityType: "account", id: "87547d08-b9d0-e911-a826-000d3a43d70a" });
// Use the request object to execute the function
Xrm.WebApi.online.execute(deleteRequest).then(
    function (result) {
        if (result.ok) {
            console.log("Status: %s %s", result.status, result.statusText);
            // perform other operations as required;
        }
    },
    function (error) {
        console.log(error.message);
        // handle error conditions
    }
);

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>microsoft</category>
      <category>powerplatform</category>
      <category>javascript</category>
      <category>webapi</category>
    </item>
    <item>
      <title>Recommendations on Git Commits and Pull Requests</title>
      <dc:creator>Mehmet Seçkin</dc:creator>
      <pubDate>Thu, 24 Jan 2019 00:00:00 +0000</pubDate>
      <link>https://dev.to/mehmetseckin/recommendations-on-git-commits-and-pull-requests-4nld</link>
      <guid>https://dev.to/mehmetseckin/recommendations-on-git-commits-and-pull-requests-4nld</guid>
      <description>&lt;h1&gt;
  
  
  Recommendations on Git Commits and Pull Requests
&lt;/h1&gt;

&lt;p&gt;A project’s long-term success is tightly coupled by its maintainability. A clean and well crafted source control history is one of the most important and powerful tools when it comes to maintain projects.&lt;/p&gt;

&lt;p&gt;In this document, I will try to provide some recommendations to help us build and keep a clean source control history and make life easier for both our future selves and whoever might inherit and maintain your codebase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commits
&lt;/h2&gt;

&lt;p&gt;The simplest building block of the source control history is the individual commits. As the commits get &lt;em&gt;better&lt;/em&gt;, your source control history gets healthier, the project becomes easier to maintain and develop, which contributes a lot to the project’s success.&lt;/p&gt;

&lt;p&gt;A good commit is an &lt;em&gt;atomic&lt;/em&gt; set of changes, documented with a well-written &lt;em&gt;commit message&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Make atomic commits
&lt;/h3&gt;

&lt;p&gt;An &lt;em&gt;atomic&lt;/em&gt; commit is a logically separated set of changes. Each set of change contains a single block of work that tackles a single task.&lt;/p&gt;

&lt;p&gt;Give some thought into your changes and try to divide them into logical chunks of work, each addressing a single operations.&lt;/p&gt;

&lt;p&gt;If you can, make your commits &lt;em&gt;digestible&lt;/em&gt;; for example, don’t code for a whole week on five different bugs and then submit all of your changes in a massive commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fix issues with single sign-on, header layout and another irrelevant module

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Try to split your work into &lt;em&gt;at least one&lt;/em&gt; commit per bug, with well-written commit messages. This will make it easier to review the changes and pull out or revert one of the change sets later if needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit a023bac42
Author: Mehmet Seckin &amp;lt;mehmetseckin@example.com&amp;gt;
Date: Fri Jan 01 00:00:00 1970 -0000

Enable single sign-on for XYZ context

Fixes: #123
---
commit 0c2cda12
Author: Mehmet Seckin &amp;lt;mehmetseckin@example.com&amp;gt;
Date: Fri Jan 02 00:00:00 1970 -0000

Improve header layout

Moved the navbar towards the top of the page to prevent the logo from
disappearing in responsive mode.

Fixes: #456
---
commit 10dcb332
Author: Mehmet Seckin &amp;lt;mehmetseckin@example.com&amp;gt;
Date: Fri Jan 03 00:00:00 1970 -0000

Add plug-in support for XYZ module

Completes: #789

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Write good commit messages
&lt;/h3&gt;

&lt;p&gt;There are some well-established conventions as to what makes a good commit message. The following example shows the idiomatic format and sections of a good commit message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summarize changes in around 50 characters or less

More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of the commit and the rest of the text as the body. The
blank line separating the summary from the body is critical (unless
you omit the body entirely); various tools like `log`, `shortlog`
and `rebase` can get confused if you run the two together.

Explain the problem that this commit is solving. Focus on why you
are making this change as opposed to how (the code explains that).
Are there side effects or other unintuitive consequences of this
change? Here's the place to explain them.

Further paragraphs come after blank lines.

 - Bullet points are okay, too

 - Typically a hyphen or asterisk is used for the bullet, preceded
by a single space, with blank lines in between, but conventions
vary here

If you use an issue tracker, put references to them at the bottom,
like this:

Resolves: #123
See also: #456, #789

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here are some rules of thumb:&lt;/p&gt;

&lt;h4&gt;
  
  
  Treat the first line of your commit message as the &lt;em&gt;Subject Line&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;The first line of your commit message should be treated like the subject line of an e-mail message.&lt;/p&gt;

&lt;p&gt;This should summarise the changes in a short, descriptive sentence.&lt;/p&gt;

&lt;h5&gt;
  
  
  Start with a capital letter
&lt;/h5&gt;

&lt;p&gt;The subject line should be capitalised, for example;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Fix file format and indentation

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h5&gt;
  
  
  Keep it short - about 50 characters or less
&lt;/h5&gt;

&lt;p&gt;50 characters isn’t an absolute limit, but it forces the author to think about the message and try to summarise it as clearly as possible.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you’re having a hard time summarizing, you might be committing too many changes at once. Always try to make atomic commits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most git platforms are aware of this convention and will display a truncated version of your subject line if it’s longer than it supposed to be.&lt;/p&gt;

&lt;h5&gt;
  
  
  Use imperative mood in the subject line
&lt;/h5&gt;

&lt;p&gt;Git itself uses imperative whenever it creates a commit, for example if you use &lt;code&gt;git merge&lt;/code&gt;, you will see a commit message like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Merge branch 'myfeature' into 'develop'

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So when you write your commit messages in the imperative, you’re following Git’s own built-in conventions, which increases &lt;strong&gt;consistency&lt;/strong&gt; across the source control history. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Refactor XYZ to improve readability
Update README file
Remove deprecated methods
Merge 'feat/abc' into 'develop'
Release v1.0.0

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;is easier to follow than the following log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Refactoring XYZ to improve readability
README file has been updated
Removal of deprecated methods
Merge 'feat/abc' into 'develop'
Released v1.0.0

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h5&gt;
  
  
  Do not end the subject line with a period
&lt;/h5&gt;

&lt;p&gt;Ending wih a period is unnecessary in subject lines. Besides, the period eats a precious character from the 50 character limit.&lt;/p&gt;

&lt;p&gt;Do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Configure Kerberos authentication

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;instead of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Configure Kerberos authentication.

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  Add a body to explain the context and details of your change
&lt;/h4&gt;

&lt;p&gt;Not every commit message requires a subject and a body. Some commits can be as simple as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Correct typo in README file

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;There is no need to say anything more. If the reader wants to know what was the typo, they can take a look at the change.&lt;/p&gt;

&lt;p&gt;However, sometimes your commit needs a bit of explanation or context. In this case, add details of your change after your subject line. Try to explain the context and reason of the change. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Re-authenticate user with admin scope

The X action requires the user to have elevated privileges. This commit
adds functionality to request the necessary privileges and perform the
action subsequently.

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h5&gt;
  
  
  Separate the body from the subject line with a blank line
&lt;/h5&gt;

&lt;p&gt;For example, &lt;code&gt;git revert&lt;/code&gt; generates the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Revert "Add the thing with the stuff"

This reverts commit cc87791524aedd593cff5a74532befe7ab69ce9d.

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h5&gt;
  
  
  Use the body to explain &lt;em&gt;what&lt;/em&gt; and &lt;em&gt;why&lt;/em&gt; instead of &lt;em&gt;how&lt;/em&gt;
&lt;/h5&gt;

&lt;p&gt;Code is generally self explanatory about &lt;em&gt;how&lt;/em&gt; a change was made. Focus on documenting the problem your solution and why is it the best way to tackle the problem in the message, instead of the implementation details of your solution.&lt;/p&gt;

&lt;p&gt;For example, do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clear the token cache when a new session starts

The cached tokens were being re-used by the browser which resulted
in token renewal errors between sessions. Clearing the token cache
avoids the futile effort by the browser to renew the previous session.

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Instead of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Clear the token cache when a new session starts

Call `OAuth2UserManager.TokenCache.Clear()` to clear the token cache.

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h5&gt;
  
  
  Add references to tasks or bugs at the end of the body
&lt;/h5&gt;

&lt;p&gt;Drop a reference to the related work item to make it much easier to track the cause of the change. This is especially good when the git platform is capable of automatically associating the referenced work item as it also helps with changelogs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pull Requests
&lt;/h2&gt;

&lt;p&gt;The Pull Requests are an important part of the source control history, because this is the place where a particular change is documented, explained, reviewed and refined.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep Pull Requests as small as possible
&lt;/h3&gt;

&lt;p&gt;The more changes in a pull request, the harder to understand it, and the longer it takes to review it.&lt;/p&gt;

&lt;p&gt;Break down your work into multiple pieces of logically separate and independent pull requests to ease the review process, catch bugs early and reduce code smell.&lt;/p&gt;

&lt;h3&gt;
  
  
  Apply single responsibility principle
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;The single responsibility principle is a computer programming principle that states that every module or class should have responsibility over a single part of the functionality provided by the software, and that responsibility should be entirely encapsulated by the class.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Just like classes and modules, pull requests should do only one thing. Before submitting a PR for review, try applying the principle of single responsibility. If this code is doing more than one thing, break it into other Pull Requests.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pull Request title should be short and self-explanatory
&lt;/h3&gt;

&lt;p&gt;The title should be sufficient to understand what is being changed. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add single sign-on support for X&lt;/li&gt;
&lt;li&gt;Improve registration error handling process&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The description should contain as much detail as possible
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Describe &lt;strong&gt;what&lt;/strong&gt; has changed.&lt;/li&gt;
&lt;li&gt;Explain &lt;strong&gt;why&lt;/strong&gt; has the change been made.&lt;/li&gt;
&lt;li&gt;Clearly define &lt;strong&gt;how&lt;/strong&gt; the change was implemented&lt;/li&gt;
&lt;li&gt;Use screenshots, code snippets and other resources to demonstrate the changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially helpful if the Pull Requests are merged into the mainstream via a &lt;em&gt;squash merge&lt;/em&gt;, in which case your PR title and description will automatically form a good commit message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;This document is heavily inspired from the following articles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://chris.beams.io/posts/git-commit/"&gt;How to Write a Git Commit Message&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.git-scm.com/book/en/v2/Distributed-Git-Contributing-to-a-Project#_commit_guidelines"&gt;Git Commit Guidelines&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hackernoon.com/the-art-of-pull-requests-6f0f099850f9"&gt;The Art of Pull Requests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@hugooodias/the-anatomy-of-a-perfect-pull-request-567382bb6067"&gt;The Anatomy of a Perfect Pull Request&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>softwarecraftsmanship</category>
    </item>
    <item>
      <title>Delete multiple Azure Active Directory applications via PowerShell</title>
      <dc:creator>Mehmet Seçkin</dc:creator>
      <pubDate>Tue, 25 Sep 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/mehmetseckin/delete-multiple-azure-active-directory-applications-via-powershell-4bm8</link>
      <guid>https://dev.to/mehmetseckin/delete-multiple-azure-active-directory-applications-via-powershell-4bm8</guid>
      <description>&lt;h1&gt;
  
  
  Delete multiple Azure Active Directory applications via PowerShell
&lt;/h1&gt;

&lt;p&gt;Recently, I needed a quick way to delete multiple Azure Active Directory applications. This is unfortunately not possible through the Azure portal, so it was time for a little PowerShell magic.&lt;/p&gt;

&lt;p&gt;I came up with a little command named &lt;code&gt;Remove-AzureADApplications&lt;/code&gt;, which will present a list of applications on your tenant and delete the selected applications. It also features a silent execution mode (&lt;code&gt;-Force&lt;/code&gt; parameter) and a &lt;code&gt;display name begins with&lt;/code&gt; filter option (&lt;code&gt;-SearchString&lt;/code&gt; parameter).&lt;/p&gt;

&lt;p&gt;Check it out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;#
  .SYNOPSIS
  Delete multiple Azure AD applications based on a search criteria.

  .DESCRIPTION
  The Remove-AzureADApplications cmdlet displays a list of all applications registered in the Azure Active Directory, and deletes specified applications from Azure Active Directory (AD).

  .PARAMETER SearchString
  The service principal search string.

  .PARAMETER Force
  Forces the command to run without asking for user confirmation. This will remove all applications that match the filter criteria specified by the SearchString parameter, without displaying a list and waiting for the user to specify which applications are going to be deleted. If the SearchString parameter is omitted, this will attempt to delete all application registrations without confirmation. Use with caution.

  .EXAMPLE
  Remove-AzureADApplications

  Displays the list of all applications registered in the Azure Active Directory, and deletes selected applications.

  .EXAMPLE
  Remove-AzureADApplications -SearchString "deleteme" -Force

  Remove applications whose names start with "deleteme" without displaying a list for the user to select.
#&amp;gt;
function Remove-AzureADApplications 
{
    [CmdletBinding()]
    param
    (
        [Parameter(HelpMessage = "The service principal search string.")]
        [string]
        $SearchString = "",    
        [Parameter(HelpMessage ="Forces the command to run without asking for user confirmation. This will remove all applications that match the filter criteria specified by the SearchString parameter. If the SearchString parameter is omitted, this will attempt to delete all application registrations without confirmation. Use with caution.")]
        [Switch]
        $Force
    )

    Import-Module "AzureAD";

    if ($SearchString) {
        $apps = (Get-AzureADApplication -SearchString $SearchString)
    }
    else {
        Write-Warning "No search string specified. Fetching all applications."
        $apps = (Get-AzureADApplication -All $true)
    }

    if($Force)
    {
        $selectedApps = $apps;
    }
    else
    {
        $selectedApps = $apps | Out-GridView -Title "Please select applications to remove..." -OutputMode Multiple;
    }

    $selectedApps | ForEach-Object {

        $displayName = $_.DisplayName;
        $objectId = $_.ObjectId;
        try {
            Remove-AzureADApplication -ObjectId $objectId
            Write-Host "Removed $displayName..." -ForegroundColor Green;
        }
        catch {
            Write-Host "Failed to remove $displayName..." -ForegroundColor Red;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>azuread</category>
      <category>powershell</category>
    </item>
    <item>
      <title>Clean up git branches that do not exist on origin</title>
      <dc:creator>Mehmet Seçkin</dc:creator>
      <pubDate>Wed, 08 Aug 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/mehmetseckin/clean-up-git-branches-that-do-not-exist-on-origin-1afj</link>
      <guid>https://dev.to/mehmetseckin/clean-up-git-branches-that-do-not-exist-on-origin-1afj</guid>
      <description>&lt;h1&gt;
  
  
  Clean up git branches that do not exist on origin
&lt;/h1&gt;

&lt;p&gt;After our team worked on a git repository for a while, we accumulated a lot of useless branches that are no longer being used. We’ve removed these branches regularly from the origin, but the local branches remained in everyone’s workstations like zombies.&lt;/p&gt;

&lt;p&gt;It was time to do something.&lt;/p&gt;

&lt;p&gt;That’s when we decided to combine the powers of &lt;code&gt;git fetch --prune&lt;/code&gt; and shell scripting to forge a PowerShell function that will kill the zombie branches in our local repositories.&lt;/p&gt;

&lt;p&gt;The script simply uses &lt;code&gt;git fetch --all --prune&lt;/code&gt; to update all remote references (&lt;code&gt;--all&lt;/code&gt;) and drop deleted ones (&lt;code&gt;--prune&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;After this, it filters out the local branches that do not exist on the origin by scraping the output of &lt;code&gt;git branch -vv&lt;/code&gt;, and deletes the zombie local branches.&lt;/p&gt;

&lt;p&gt;Here’s the whole thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;#
.SYNOPSIS
Remove local git branches that are deleted from the origin.

.DESCRIPTION
Removes local git branches that are deleted from the origin using `git fetch --prune` and `git branch -[dD]`.

.PARAMETER Force
Switch to to force removal using `git branch -D` instead of `git branch -d`.

.EXAMPLE
Remove-DeletedGitBranches
Removes merged non-existing branches.

.EXAMPLE
Remove-DeletedGitBranches -Force
Removes all non-existing branches.

.NOTES
This cmdlet uses `git fetch --prune`, so it will delete references to non-existing branches in the process. Use with caution.
#&amp;gt;
function Remove-DeletedGitBranches
{
    param
    (
        [Parameter()]
        [Switch]
        $Force
    )

    $null = (git fetch --all --prune);
    $branches = git branch -vv | Select-String -Pattern ": gone]" | ForEach-Object { $_.toString().Split(" ")[2] };
    if($Force)
    {
        $branches | ForEach-Object {git branch -D $_};
    }
    else 
    {        
        $branches | ForEach-Object {git branch -d $_};        
    }
}

Set-Alias -Name "rmdelbr" -Value Remove-DeletedGitBranches
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



</description>
      <category>git</category>
      <category>cleanup</category>
      <category>shortcut</category>
      <category>powershell</category>
    </item>
    <item>
      <title>Extending Dynamics 365 Package Deployer</title>
      <dc:creator>Mehmet Seçkin</dc:creator>
      <pubDate>Mon, 25 Jun 2018 00:00:00 +0000</pubDate>
      <link>https://dev.to/mehmetseckin/extending-dynamics-365-package-deployer-3i7f</link>
      <guid>https://dev.to/mehmetseckin/extending-dynamics-365-package-deployer-3i7f</guid>
      <description>&lt;h1&gt;
  
  
  Extending Dynamics 365 Package Deployer
&lt;/h1&gt;

&lt;p&gt;25 June 2018, Windsor, UK&lt;/p&gt;

&lt;p&gt;The Dynamics 365 Package Deployer is a great tool that allows you to move your customisations and data between environments in bulk.&lt;/p&gt;

&lt;p&gt;It imports your solutions and data, and provides an API to hook your custom code up to this process at certain points. These hooks are as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Rt8c3mYD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://mehmetseckin.com/images/dynamics-365-package-deployer-hooks.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Rt8c3mYD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://mehmetseckin.com/images/dynamics-365-package-deployer-hooks.png" alt="Custom code hooks for Dynamics 365 Package Deployer process"&gt;&lt;/a&gt;&lt;em&gt;Custom code hooks for Dynamics 365 Package Deployer process&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method Name&lt;/th&gt;
&lt;th&gt;Execution Time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;InitializeCustomExtension&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After the package deployer extension is initialized, before the solution import process.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PreSolutionImport&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Before importing each solution.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;RunSolutionUpgradeMigrationStep&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Before importing each solution, only if the solution that is being imported is already present in the target Dynamics 365 instance.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;BeforeImportStage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Before the solution import completes, if the sample data or flat files are being imported.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AfterPrimaryImport&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After the solution and data imports are completed.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are some special parameters and properties that you can set in some of these methods. These parameters and properties can be set to alter the deployment behaviour, such has enabling plug-ins and workflows disabled, overriding safety checks during data import, etc.&lt;/p&gt;

&lt;p&gt;There are also some cool methods that lets you interfere with the user interface, such as the &lt;code&gt;CreateProgressItem&lt;/code&gt;, &lt;code&gt;RaiseUpdateEvent&lt;/code&gt;, &lt;code&gt;RaiseFailEvent&lt;/code&gt; methods. These methods enables you to create your own progress items in the progress items pane, however they seem to be only effective after the solution import process begins.&lt;/p&gt;

&lt;p&gt;Another cool property that is provided via the Dynamics 365 Package Deployer API is &lt;code&gt;RootControlDispatcher&lt;/code&gt;. This property allows you invoke logic in the Package Deployer’s main UI thread. This means, you can use your custom WPF user interface during a package deployment.&lt;/p&gt;

&lt;p&gt;The Package Deployer also supports customised Welcome and End HTML pages for license agreements, release notes and whatnot. This also supports javascript, however I haven’t tried to run a full-blown single page application on there, and I believe the javascript support would be somewhat limited.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RtoAfVwZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://mehmetseckin.com/images/dynamics-365-package-deployer-script-alert.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RtoAfVwZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/http://mehmetseckin.com/images/dynamics-365-package-deployer-script-alert.png" alt="A JavaScript alert from a custom welcome page in Dynamics 365 deployment package"&gt;&lt;/a&gt;&lt;em&gt;A JavaScript alert from a custom welcome page in Dynamics 365 deployment package&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;There is a lot of clever stuff that can be done using these extension points if you want to customise, enhance and automate the deployment process of your Dynamics 365 applications.&lt;/p&gt;

</description>
      <category>microsoft</category>
      <category>dynamics365</category>
    </item>
  </channel>
</rss>
