{"id":14381,"date":"2022-06-14T13:43:20","date_gmt":"2022-06-14T20:43:20","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/cse\/?p=14381"},"modified":"2023-06-19T11:20:57","modified_gmt":"2023-06-19T18:20:57","slug":"xpp-and-git","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/ise\/xpp-and-git\/","title":{"rendered":"X++ And Git: A Basic Setup"},"content":{"rendered":"<h2>Overview<\/h2>\n<p>In this post we outline the basics of setting up an X++ repository in Git as well as a YAML-based build pipeline which executes against this repository.<\/p>\n<h2>Background<\/h2>\n<p>Git is an increasingly popular version control system which sees broad use within the Commercial Software Engineering (CSE) organization at Microsoft. Dynamics 365 for Finance and Operations (D365 F&amp;O) projects have deployed with Team Foundation Version Control (TFVC) as the default repository technology for several years, and TFVC remains the default version control option for F&amp;O development projects to this day. Given the growing developer preference toward Git, we prototyped a Git repo setup on a recent X++ development project. We are sharing our setup steps here for broader reuse.<\/p>\n<h2>Objectives<\/h2>\n<p>Our primary objective for this portion of the project was to design and implement a combined X++ and C# repo which embodied the relevant CSE fundamentals outlined <a href=\"https:\/\/microsoft.github.io\/code-with-engineering-playbook\/ENG-FUNDAMENTALS-CHECKLIST\/\">here<\/a>.<\/p>\n<p>As is sometimes the case on X++ development projects, this project had a dependency on C# code to handle some integration scenarios. Consequently, the repository structure outlined in this post reflects the unique scenario where we both needed to have source access to the C# code, and we wanted to be able to build it alongside the X++ code every time we created a new software deployable package. This guidance can still be used to set up an X++-only repository.<\/p>\n<p>Because we chose to focus on current trends, there are several important areas that we did not explore and are not covered by this post, namely:<\/p>\n<ul>\n<li>Continuous Deployment (CD) \u2013 We did not need automated CD for this project &#8211; there are known patterns for setting up CD using the Microsoft-provided Azure DevOps (AZDO) extension <a href=\"https:\/\/docs.microsoft.com\/en-us\/dynamics365\/fin-ops-core\/dev-itpro\/dev-tools\/pipeline-deploy-asset\">here<\/a>.<\/li>\n<li>Automated testing \u2013 Automated testing requires a dedicated build Virtual Machine (VM) and special modifications to the build pipeline definition, or the creation of a separate pipeline altogether.<\/li>\n<\/ul>\n<h2>Prerequisites<\/h2>\n<ul>\n<li>Basic knowledge of the X++ source file directory structure on a developer machine is required to correctly locate, move, and version the relevant models.<\/li>\n<li>Basic knowledge of Git is strongly recommended, as it will add context to the setup steps outlined in this post.<\/li>\n<\/ul>\n<hr \/>\n<h2>Walkthrough<\/h2>\n<p><em>Note: It is a best practice to generate deployable packages via build pipeline versus locally generating packages on dev environments. Thus, this walkthrough outlines both repo and build pipeline setup.<\/em><\/p>\n<h3><strong>Repository Structure<\/strong><\/h3>\n<p>We used the below structure for the project repository, which prioritizes high organization at the root level of the structure. This structure is not mandatory for managing X++ in Git and we mention it here for context only.<\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitRepoStructure.png\"><img decoding=\"async\" class=\"aligncenter wp-image-14385 size-full\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitRepoStructure.png\" alt=\"Repository structure diagram\" width=\"750\" height=\"742\" srcset=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitRepoStructure.png 750w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitRepoStructure-300x297.png 300w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitRepoStructure-24x24.png 24w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitRepoStructure-48x48.png 48w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitRepoStructure-96x96.png 96w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/a><\/p>\n<p style=\"text-align: center;\"><strong>Figure 1: X++ Git Repository Structure<\/strong><\/p>\n<p>&nbsp;<\/p>\n<h3><strong>Set up the Git Repository<\/strong><\/h3>\n<ol>\n<li><strong>Create a new repo in AZDO.<\/strong>\n<ol style=\"list-style-type: lower-alpha;\">\n<li>Identify the AZDO project where you will configure the repository, then follow the instructions <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/devops\/repos\/git\/create-new-repo?view=azure-devops\">here<\/a> to create a new repository. Stop at step 2 of the <em>Clone the repo to your computer<\/em> section, as the repo cloning will require some special adjustments for the D365 F&amp;O directory structure. Record the URL of your repo as specified in step 2 of the instructions.<\/li>\n<\/ol>\n<\/li>\n<li><strong>Identify the X++ model that will be versioned<\/strong>.\n<ol style=\"list-style-type: lower-alpha;\">\n<li>If you are creating a new X++ model for this exercise, follow the instructions <a href=\"https:\/\/docs.microsoft.com\/en-us\/dynamics365\/fin-ops-core\/dev-itpro\/dev-tools\/create-data-model-elements\">here<\/a> to generate the model before moving on to the next step.<\/li>\n<li>Note the name of the top-level directory for the model you want to version, which can usually be found under the <em>K:\\AosService\\PackagesLocalDirectory <\/em>location on a dev machine.<\/li>\n<\/ol>\n<\/li>\n<li><strong>Clone the repo<\/strong><\/li>\n<\/ol>\n<p style=\"padding-left: 40px;\"><em style=\"font-size: 1rem;\">Note: these instructions outline Git repo setup using Visual Studio 2019. There are alternate setup options available, such as using the Git command line.<\/em><\/p>\n<p><strong><em> <a href=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2022\/06\/VSCloneRepo.png\"><img decoding=\"async\" class=\"size-full wp-image-14383 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2022\/06\/VSCloneRepo.png\" alt=\"Visual Studio 'Clone a repository' dialog\" width=\"480\" height=\"332\" srcset=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2022\/06\/VSCloneRepo.png 480w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2022\/06\/VSCloneRepo-300x208.png 300w\" sizes=\"(max-width: 480px) 100vw, 480px\" \/><\/a><\/em><\/strong><\/p>\n<p style=\"text-align: center;\"><strong>Figure 2: Clone Repository dialog in Visual Studio<\/strong><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol style=\"list-style-type: lower-alpha;\">\n<li>In Visual Studio, navigate to <em>Git &gt; Clone.<\/em><\/li>\n<li>In <em>Path<\/em>, enter the local destination path where you want to store the model. By default Visual Studio 2019 will select a location that looks like <em>C:\\Users\\&lt;Your User&gt;\\source\\repos<\/em>. Record this directory location.<\/li>\n<li>In <em>Repository location<\/em>, enter the URL of the repo you created in step 1 of these instructions. <em>Note: if you populate the repository location <strong>after<\/strong> the path, the unique name of your repo should automatically be added to the directory path.<\/em> Ensure this is the name you want, and change it if desired.<\/li>\n<li>Click <em>Clone<\/em>.<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<ol start=\"4\">\n<li><strong>Move the source directories and set up symbolic links<\/strong>.<\/li>\n<\/ol>\n<p style=\"padding-left: 40px;\"><em>Note: To address Git&#8217;s preference for an empty repository location while also maintaining Application Object Server (AOS) visibility to the pertinent custom source files, we employed a workaround involving a file system tool called <\/em><a href=\"https:\/\/docs.microsoft.com\/en-us\/windows\/win32\/fileio\/creating-symbolic-links\"><em>Symbolic Links<\/em><\/a><em>.\u00a0 Symbolic Links are in essence \u201cdeep shortcuts\u201d between directories. A .gitignore file excluding the system-standard models would also address this issue.<\/em><\/p>\n<ol>\n<li style=\"list-style-type: none;\">\n<ol style=\"list-style-type: lower-alpha;\">\n<li><strong>Move the model directories. <\/strong>For each of the model folders identified in step 2:\n<ol>\n<li>Find the path to the top-level model directory. To find Model1 from the example repo structure above, we would usually search somewhere like <em>K:\\AosService\\PackagesLocalDirectory\\Model1<\/em> .<\/li>\n<li>Using either command line or File Explorer, move the model folder from <em>K:\\AosService\\PackagesLocalDirectory <\/em>to the target folder you created and recorded in step 3. <em>Note: if you are moving an existing model, the folder contents may be in-use. You will need to shut down the AOS and related services before you are allowed to move the entire folder contents.<\/em><\/li>\n<\/ol>\n<\/li>\n<li><strong>Set up symbolic links. <\/strong>Each moved model directory will need a symbolic link to point the D365 services to its new location. To do this, open a Command Prompt in admin mode, navigate to the <em>PackagesLocalDirectory <\/em>folder, and then, for each folder you moved, enter the following command:<\/li>\n<\/ol>\n<\/li>\n<\/ol>\n<p style=\"padding-left: 120px;\"><em>&gt;mklink \/D \u201cThe exact name of the model folder\u201d \u201cThe file path to the new folder location\u201d<\/em><\/p>\n<p style=\"padding-left: 80px;\">For example, if you used all of the defaults outlined above, the command would look something like:<\/p>\n<p style=\"padding-left: 120px;\"><em>&gt;mklink \/D \u201cModel1\u201d \u201cC:\\Users\\&lt;Your User&gt;\\source\\repos\\XppGitRepo\u201d<\/em><\/p>\n<p style=\"padding-left: 80px;\">You can validate the links manually by navigating to the new locations starting at the AOS folder, either via File Explorer or command line. The model folder contents should look like they exist under the AOS folder<strong>.<\/strong><\/p>\n<hr \/>\n<h3><strong>Build Pipeline Overview<\/strong><\/h3>\n<p>The X++ build pipeline we created for this project uses the Microsoft-hosted agent build approach outlined <a href=\"https:\/\/docs.microsoft.com\/en-us\/dynamics365\/fin-ops-core\/dev-itpro\/dev-tools\/hosted-build-automation\">here<\/a>, and it is based on the sample build definitions available <a href=\"https:\/\/github.com\/microsoft\/Dynamics365-Xpp-Samples-Tools\/tree\/master\/CI-CD\/Pipeline-Samples\">here<\/a>.<\/p>\n<p>The main advantage of this approach is that it does not require a dedicated build VM to generate deployable packages.\u00a0 The main disadvantage of this approach is that it does not natively support automated F&amp;O test execution.<\/p>\n<p>Technically, this pipeline can be created as either a \u201cClassic\u201d (non-YAML) pipeline, or a YAML-based scripted pipeline; for improved modularity and maintainability, this walkthrough uses the latter approach.<\/p>\n<p>The pipeline executes two main jobs which:<\/p>\n<ol>\n<li>Create a production-ready package which excludes test models<\/li>\n<li>Create an automated test-ready package which includes all models<\/li>\n<\/ol>\n<p>The same build definition generates both packages to avoid any potential code or configuration drift between test-bound and production-bound packages.<\/p>\n<p>To uniquely identify test versus non-test models in the Visual Studio solution referenced by the build pipeline, we employed Visual Studio\u2019s native solution configuration features to map production-bound models in the \u2018release\u2019 configuration, and test-bound models in the \u2018debug\u2019 configuration. More information on solution configurations can be found <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/ide\/understanding-build-configurations?view=vs-2022\">here.<\/a><\/p>\n<p>&nbsp;<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitPipelineOverview.png\"><img decoding=\"async\" class=\"size-full wp-image-14384 aligncenter\" src=\"https:\/\/devblogs.microsoft.com\/cse\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitPipelineOverview.png\" alt=\"Build pipeline diagram\" width=\"750\" height=\"366\" srcset=\"https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitPipelineOverview.png 750w, https:\/\/devblogs.microsoft.com\/ise\/wp-content\/uploads\/sites\/55\/2022\/06\/XppGitPipelineOverview-300x146.png 300w\" sizes=\"(max-width: 750px) 100vw, 750px\" \/><\/a><\/p>\n<p style=\"text-align: center;\"><strong>Figure 3: Simplified build pipeline diagram<\/strong><\/p>\n<p><strong>\u00a0<\/strong><\/p>\n<h3><strong>Build Pipeline Scripts and Configuration<\/strong><\/h3>\n<p>Build pipelines are complex, and YAML-based pipelines are dependent on repository structure, so rather than provide a step-by-step walkthrough, we are providing detailed explanations along with the YAML file contents, in dependency order, as a starting point for you to build your own pipeline.<\/p>\n<h4><strong>packages.config<\/strong><\/h4>\n<p><strong>Description<\/strong>: Specifies the NuGet packages and versions used to set up both the compiler tools and system-standard source files (upon which the build pipeline depends to generate compiled X++ source code).<\/p>\n<p><em>Note: As noted in <\/em><a href=\"https:\/\/docs.microsoft.com\/en-us\/dynamics365\/fin-ops-core\/dev-itpro\/dev-tools\/hosted-build-automation\"><em>the documentation on hosted build automation<\/em><\/a><em>, these packages must be manually uploaded to an AZDO artifact feed in your own AZDO project before the references can be successfully retrieved by the build pipeline. Also, you will need to update the highlighted version numbers within your config file to match the application and platform versions you want to compile against.<\/em><\/p>\n<p><strong>Contents:<\/strong><\/p>\n<pre class=\"prettyprint\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\r\n&lt;packages&gt;\r\n    &lt;package id=\"Microsoft.Dynamics.AX.Platform.DevALM.BuildXpp\" version=\"<mark>7.0.6253.76<\/mark>\" targetFramework=\"netstandard1.6\" \/&gt;\r\n    &lt;package id=\"Microsoft.Dynamics.AX.Application.DevALM.BuildXpp\" version=\"<mark>10.0.1084.80<\/mark>\" targetFramework=\"netstandard1.6\" \/&gt;\r\n    &lt;package id=\"Microsoft.Dynamics.AX.ApplicationSuite.DevALM.BuildXpp\" version=\"<mark>10.0.1084.80<\/mark>\" targetFramework=\"netstandard1.6\" \/&gt;\r\n    &lt;package id=\"Microsoft.Dynamics.AX.Platform.CompilerPackage\" version=\"<mark>7.0.6253.76<\/mark>\" targetFramework=\"netstandard1.6\" \/&gt;\r\n&lt;\/packages&gt;<\/pre>\n<p><strong>\u00a0<\/strong><\/p>\n<h4><strong>nuget.config<\/strong><\/h4>\n<p><strong>Description<\/strong>: Specifies the AZDO artifact feed to be used for retrieval of the packages specified in packages.config. <em>Note: You will need to update the target URL and optionally the key name to match your artifact feed.<\/em><\/p>\n<p><strong>Contents:<\/strong><\/p>\n<pre class=\"prettyprint\">&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\r\n&lt;configuration&gt;\r\n    &lt;packageSources&gt;\r\n        &lt;clear \/&gt;\r\n        &lt;add key=\"<mark>XppBuildDependencies<\/mark>\" value=\"https:\/\/pkgs.dev.azure.com\/<mark>PROJECTCOLLECTIONNAME\/PROJECTGUID<\/mark>\/_packaging\/<mark>ARTIFACTFEEDNAME<\/mark>\/nuget\/v3\/index.json\" \/&gt;\r\n    &lt;\/packageSources&gt;\r\n&lt;\/configuration&gt;\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h4><strong>Jobs-xpp-build.yaml<\/strong><\/h4>\n<p><strong>Description<\/strong>: Sets up NuGet dependencies, generates a unique model version ID for the compiled result, performs a special execution of VSBuild using the X++ compiler tools and source files specified in packages.config, records the compile logs, and optionally generates a deployable package and publishes the package to the pipeline results.<\/p>\n<p><em>Note: we added conditions to the last two steps to separate the compilation of test models from production models. While this particular pipeline does not perform any additional steps in the test job, it serves as a structural example of how test execution and package generation could be separated and fully executed in a more advanced pipeline implementation.<\/em><\/p>\n<p><strong>Contents:<\/strong><\/p>\n<pre class=\"prettyprint\">parameters:\r\n  jobName: 'buildxppmodel' # Overridable name for job.\r\n  displayName: 'Build X++ Package' # Overridable display name for job.\r\n  xppModelSolution: '' # X++ model solution to build\r\n  metadataPath: '' # Location of X++ model metadata\r\n  nugetConfigsPath: '' # Location of X++ model nugets config\r\n  configuration: 'debug' # Build configuration\r\n  package: 'false' # Whether to package the model as an artifact\r\n\r\njobs:\r\n- job: ${{ parameters.jobName }}\r\n  displayName: ${{ parameters.displayName }}\r\n\r\n  variables:\r\n    AppPackage: 'Microsoft.Dynamics.AX.Application.DevALM.BuildXpp'\r\n    AppSuitePackage: 'Microsoft.Dynamics.AX.ApplicationSuite.DevALM.BuildXpp'\r\n    PlatPackage: 'Microsoft.Dynamics.AX.Platform.DevALM.BuildXpp'\r\n    ToolsPackage: 'Microsoft.Dynamics.AX.Platform.CompilerPackage'\r\n    NugetsPath: '$(Pipeline.Workspace)\\NuGets'\r\n\r\n  steps:\r\n  - task: NuGetInstaller@0\r\n    displayName: NuGet Install X++ Packages\r\n    inputs:\r\n      solution: ${{ parameters.nugetConfigsPath }}\\packages.config\r\n      nugetConfigPath: ${{ parameters.nugetConfigsPath }}\\nuget.config\r\n      restoreMode: install\r\n      nuGetRestoreArgs: -ExcludeVersion -OutputDirectory \"$(NugetsPath)\"\r\n\r\n  - task: XppUpdateModelVersion@0\r\n    displayName: Update Model Version\r\n\r\n  - task: VSBuild@1\r\n    displayName: Build X++ Model\r\n    inputs:\r\n      configuration: ${{ parameters.configuration }}\r\n      solution: ${{ parameters.xppModelSolution }}\r\n      msbuildArgs: \/restore \/p:BuildTasksDirectory=\"$(NugetsPath)\\$(ToolsPackage)\\DevAlm\" \/p:MetadataDirectory=\"${{ parameters.metadataPath }}\" \/p:FrameworkDirectory=\"$(NuGetsPath)\\$(ToolsPackage)\" \/p:ReferenceFolder=\"$(NuGetsPath)\\$(PlatPackage)\\ref\\net40;$(NuGetsPath)\\$(AppPackage)\\ref\\net40;$(NuGetsPath)\\$(AppSuitePackage)\\ref\\net40;${{ parameters.metadataPath }};$(Build.BinariesDirectory)\" \/p:ReferencePath=\"$(NuGetsPath)\\$(ToolsPackage)\" \/p:OutputDirectory=\"$(Build.BinariesDirectory)\"\r\n\r\n  - task: CopyFiles@2\r\n    displayName: 'Copy X++ Compile Log Files for Artifact Publishing'\r\n    inputs:\r\n      SourceFolder: $(Build.SourcesDirectory)\r\n      Contents: |\r\n        **\\Dynamics.AX.*.xppc.*\r\n        **\\Dynamics.AX.*.labelc.*\r\n        **\\Dynamics.AX.*.reportsc.*\r\n      TargetFolder: $(Build.ArtifactStagingDirectory)\\Logs\\${{ parameters.configuration }}\\\r\n    condition: and(succeeded(), eq('${{ parameters.package }}', 'true'))\r\n\r\n  - task: XppCreatePackage@0\r\n    displayName: Create Deployable Package\r\n    inputs:\r\n      XppToolsPath: $(NuGetsPath)\\$(ToolsPackage)\r\n      XppBinariesPath: $(Build.BinariesDirectory)\r\n      XppBinariesSearch: '*'\r\n      DeployablePackagePath: $(Build.ArtifactStagingDirectory)\\AXDeployableRuntime_$(Build.BuildNumber)-${{ parameters.configuration }}.zip\r\n    condition: and(succeeded(), eq('${{ parameters.package }}', 'true'))\r\n\r\n  - task: PublishBuildArtifacts@1\r\n    displayName: 'Publish Artifacts'\r\n    inputs:\r\n      PathtoPublish: '$(Build.ArtifactStagingDirectory)'\r\n    condition: and(succeeded(), eq('${{ parameters.package }}', 'true'))\r\n<\/pre>\n<p>&nbsp;<\/p>\n<h4><strong>Azure-pipeline-xpp.yaml<\/strong><\/h4>\n<p><strong>Description<\/strong>: Triggered by changes to the target branch. Collects X++ build-relevant details such as the Visual Studio solution and the location of the X++ metadata and AZDO artifact feed. Passes these details through to a job which performs the actual model compilation and, optionally, packages the results.<\/p>\n<p><strong>Contents<\/strong>:<\/p>\n<pre class=\"prettyprint\"># Name must be in #.#.#.# format for X++ tasks\r\nname: $(Date:yy.MM.dd)$(Rev:.r)\r\n\r\ntrigger:\r\n- main\r\n\r\npool:\r\n  name: Azure Pipelines\r\n  vmImage: 'windows-2022'\r\n  demands:s\r\n    - msbuild\r\n    - visualstudio\r\n\r\nvariables:\r\n  MetadataPath: '$(Build.SourcesDirectory)\\src\\xplusplus\\Models'\r\n  NugetConfigsPath: '$(Build.SourcesDirectory)\\src\\xplusplus\\XppBuild'\r\n  XppModelSolution: 'src\/xplusplus\/xppBuild\/&lt;your solution name here&gt;.sln'\r\n\r\nstages:\r\n\r\n- stage: buildxppmodel\r\n  displayName: 'Build XPP Model'\r\n  dependsOn: []\r\n  jobs:\r\n\r\n  - template: templates\/jobs-xpp-build.yaml\r\n    parameters:\r\n      jobName: 'buildxppallmodels'\r\n      displayName: 'Build X++ All Models'\r\n      xppModelSolution: '$(XppModelSolution)'\r\n      metadataPath: '$(MetadataPath)'\r\n      nugetConfigsPath: '$(NugetConfigsPath)'\r\n\r\n  - template: templates\/jobs-xpp-build.yaml\r\n    parameters:\r\n      jobName: 'buildxppdeploymentpackage'\r\n      displayName: 'Build X++ Deployment Package'\r\n      xppModelSolution: '$(XppModelSolution)'\r\n      metadataPath: '$(MetadataPath)'\r\n      nugetConfigsPath: '$(NugetConfigsPath)'\r\n      configuration: 'release'\r\n      package: 'true'<\/pre>\n<hr \/>\n<h2>Summary<\/h2>\n<p>In this post we outlined the basics of setting up an X++ repository in Git as well as a YAML-based build pipeline which executes against this repository.<\/p>\n<h2>The Team<\/h2>\n<p>Many thanks to <a href=\"https:\/\/www.linkedin.com\/in\/sujitdmello\/\">Sujit D\u2019Mello<\/a> for starting this work with me, <a href=\"https:\/\/www.linkedin.com\/in\/jose-rodriguez-30558423\/\">Jose Rodriguez<\/a> for helping me finish it, <a href=\"https:\/\/www.linkedin.com\/in\/jorisdg\/\">Joris de Gruyter<\/a> for leading the way on many of the tools and patterns we built upon for this example, and to the X++ developer community for experimenting with many of these concepts well ahead of this post\u2019s creation.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Overview In this post we outline the basics of setting up an X++ repository in Git as well as a YAML-based build pipeline which executes against this repository. Background Git is an increasingly popular version control system which sees broad use within the Commercial Software Engineering (CSE) organization at Microsoft. Dynamics 365 for Finance and [&hellip;]<\/p>\n","protected":false},"author":93828,"featured_media":14409,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3356,1,16],"tags":[3360,3359,3362,3361,113,151,3317,3357,3358],"class_list":["post-14381","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-business-applications","category-cse","category-devops","tag-azdo","tag-azure-devops","tag-biz-apps","tag-business-applications","tag-c","tag-devops","tag-git","tag-x","tag-xpp"],"acf":[],"blog_post_summary":"<p>Overview In this post we outline the basics of setting up an X++ repository in Git as well as a YAML-based build pipeline which executes against this repository. Background Git is an increasingly popular version control system which sees broad use within the Commercial Software Engineering (CSE) organization at Microsoft. Dynamics 365 for Finance and [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/14381","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/users\/93828"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/comments?post=14381"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/posts\/14381\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media\/14409"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/media?parent=14381"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/categories?post=14381"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/ise\/wp-json\/wp\/v2\/tags?post=14381"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}