quarta-feira, 31 de agosto de 2022

Como recuperar mais detalhes sobre um erro, através do Environment monitoring

Por vezes a tratativa de exceção do desenvolvedor não nos dá ideia do que ocorreu em determinado processamento em que houve falha, diante desta possibilidade precisamos recorrer a uma ferramenta que está disponível em ambientes UAT e de Produção que é o Enviroment Monitoring. 

Para acessar o Enviroment Monitoring, acesse a página de seu ambiente via LCS e desça até o final. Haverá um hyperlink como na imagem abaixo:


Selecione o hyperlink citado acima e aparecerá uma tela com uma aba chamada Activity, selecione-a altere os parâmetros como falarei a seguir.

É muito importante que seu filtro busque informações no momento exato que determinada exception ocorreu. Caso você não tenha essa informação, peça para que um usuário execute o processo mais uma vez e lhe passe a hora exata em que isso foi feito. Com o horário em mãos, atribua um range nos campos Start date (UTC) e End date (UTC) de forma a garantir que aquele momento em que a exception ocorreu esteja incluso neste período. Caso apareça o erro em um dialog, peça para o usuário lhe passar o activity id que aparecer, ele te ajuda a garimpar as informações quando tiver o excel do log raw em mãos.

Estamos aqui tentando achar uma agulha no palheiro, então é importante também alterar para que o Row Limit seja o máximo suportado, de 5000. Para que esse campo apareça selecione o botão "Show Options".



Você também pode selecionar mais colunas do que as que vem por padrão, selecionando o botão "Show Column Options":




Atribuidos os filtros e parâmetros, selecione o botão "Search" e em seguida o "Export Grid". O "Export Grid" vai lhe gerar um excel com as mesmas infos que aparecem no relatório do LCS. Eu prefiro utilizar o excel para buscar as informações.

A partir dai, utilize a imaginação para filtrar as informações da planilha de modo a encontrar o que você precisa. Você pode utilizar o activity id citado anteriormente, ou qualquer outra coluna para realizar a busca. 

O que eu faço geralmente e que funciona pra mim é ir até à coluna infoMessage, aplicar um filtro nela e navegar pelas informações agrupadas que o filtro me trouxer, geralmente lá aparecerá uma mensagem mais amigável das exceptions que ocorreram naquele range de tempo do filtro (por isso é importante limitar o máximo possível tal range, quanto menor menos log gerado e mais fácil será encontrar o que você precisa):



terça-feira, 5 de julho de 2022

Implantando Pacotes automaticamente em SandBox - D365 F&O

 Na postagem anterior, expliquei como é feito o build e empacotamento através de um Pipeline no Azure. Mas dai vem a dúvida. - Eu vou ter que subir aquele pacote gerado pelo pipeline manualmente pelo LCS, me minha SandBox? A resposta é: Não!

Você precisará criar um Release no Azure DevOps. Para isso, importe o seguinte json em seu projeto no Azure DevOps:

{"source":2,"revision":34,"description":null,"createdBy":{"displayName":"Otavio Anaga","url":"https://spsprodsbr1.vssps.visualstudio.com/A56f87ddd-07a4-409d-8f49-f17f22b12cbb/_apis/Identities/774c7aa6-f081-6c47-beb5-22138886fbe7","_links":{"avatar":{"href":"https://dev.azure.com/ContosoD365/_apis/GraphProfile/MemberAvatars/aad.Nzc0YzdhYTYtZjA4MS03YzQ3LWJlYjUtMjIxMzg4ODZmYmU3"}},"id":"774c7aa6-f081-6c47-beb5-22138886fbe7","uniqueName":"otavioanaga@Contoso.com.br","imageUrl":"https://dev.azure.com/ContosoD365/_apis/GraphProfile/MemberAvatars/aad.Nzc0YzdhYTYtZjA4MS03YzQ3LWJlYjUtMjIxMzg4ODZmYmU3","descriptor":"aad.Nzc0YzdhYTYtZjA4MS03YzQ3LWJlYjUtMjIxMzg4ODZmYmU3"},"createdOn":"2021-10-23T18:02:00.000Z","modifiedBy":{"displayName":"Otavio Anaga Anaga","url":"https://spsprodsbr1.vssps.visualstudio.com/A56f87ddd-07a4-409d-8f49-f17f22b12cbb/_apis/Identities/774c7aa6-f081-6c47-beb5-22138886fbe7","_links":{"avatar":{"href":"https://dev.azure.com/ContosoD365/_apis/GraphProfile/MemberAvatars/aad.Nzc0YzdhYTYtZjA4MS03YzQ3LWJlYjUtMjIxMzg4ODZmYmU3"}},"id":"774c7aa6-f081-6c47-beb5-22138886fbe7","uniqueName":"otavioanaga@Contoso.com.br","imageUrl":"https://dev.azure.com/ContosoD365/_apis/GraphProfile/MemberAvatars/aad.Nzc0YzdhYTYtZjA4MS03YzQ3LWJlYjUtMjIxMzg4ODZmYmU3","descriptor":"aad.Nzc0YzdhYTYtZjA4MS03YzQ3LWJlYjUtMjIxMzg4ODZmYmU3"},"modifiedOn":"2022-07-05T20:58:06.397Z","isDeleted":false,"lastRelease":{"id":211,"name":"Release-211","artifacts":[],"_links":{},"description":"Triggered by Release to SandBox 22.07.05.1.","releaseDefinition":{"id":1,"projectReference":null,"_links":{}},"createdOn":"2022-07-05T08:43:57.910Z","createdBy":{"displayName":"Microsoft.VisualStudio.Services.TFS","url":"https://spsprodsbr1.vssps.visualstudio.com/A56f87ddd-07a4-409d-8f49-f17f22b12cbb/_apis/Identities/00000002-0000-8888-8000-000000000000","_links":{"avatar":{"href":"https://dev.azure.com/ContosoD365/_apis/GraphProfile/MemberAvatars/s2s.MDAwMDAwMDItMDAwMC04ODg4LTgwMDAtMDAwMDAwMDAwMDAwQDJjODk1OTA4LTA0ZTAtNDk1Mi04OWZkLTU0YjAwNDZkNjI4OA"}},"id":"00000002-0000-8888-8000-000000000000","uniqueName":"00000002-0000-8888-8000-000000000000@2c895908-04e0-4952-89fd-54b0046d6288","imageUrl":"https://dev.azure.com/ContosoD365/_apis/GraphProfile/MemberAvatars/s2s.MDAwMDAwMDItMDAwMC04ODg4LTgwMDAtMDAwMDAwMDAwMDAwQDJjODk1OTA4LTA0ZTAtNDk1Mi04OWZkLTU0YjAwNDZkNjI4OA","descriptor":"s2s.MDAwMDAwMDItMDAwMC04ODg4LTgwMDAtMDAwMDAwMDAwMDAwQDJjODk1OTA4LTA0ZTAtNDk1Mi04OWZkLTU0YjAwNDZkNjI4OA"}},"variables":{"Client":{"value":"Contoso"},"LCSEnvironmentId":{"value":"5780497e-254b-4b6e-8219-850d646e9918"},"LCsProjectId":{"value":"1533614"}},"variableGroups":[],"environments":[{"id":1,"name":"Build to LCS","rank":1,"owner":{"displayName":"Otavio Anaga","url":"https://spsprodsbr1.vssps.visualstudio.com/A56f87ddd-07a4-409d-8f49-f17f22b12cbb/_apis/Identities/774c7aa6-f081-6c47-beb5-22138886fbe7","_links":{"avatar":{"href":"https://dev.azure.com/ContosoD365/_apis/GraphProfile/MemberAvatars/aad.Nzc0YzdhYTYtZjA4MS03YzQ3LWJlYjUtMjIxMzg4ODZmYmU3"}},"id":"774c7aa6-f081-6c47-beb5-22138886fbe7","uniqueName":"otavioanaga@Contoso.com.br","imageUrl":"https://dev.azure.com/ContosoD365/_apis/GraphProfile/MemberAvatars/aad.Nzc0YzdhYTYtZjA4MS03YzQ3LWJlYjUtMjIxMzg4ODZmYmU3","descriptor":"aad.Nzc0YzdhYTYtZjA4MS03YzQ3LWJlYjUtMjIxMzg4ODZmYmU3"},"variables":{},"variableGroups":[],"preDeployApprovals":{"approvals":[{"rank":1,"isAutomated":true,"isNotificationOn":false,"id":1}],"approvalOptions":{"requiredApproverCount":null,"releaseCreatorCanBeApprover":false,"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped":false,"enforceIdentityRevalidation":false,"timeoutInMinutes":0,"executionOrder":1}},"deployStep":{"id":2},"postDeployApprovals":{"approvals":[{"rank":1,"isAutomated":true,"isNotificationOn":false,"id":3}],"approvalOptions":{"requiredApproverCount":null,"releaseCreatorCanBeApprover":false,"autoTriggeredAndPreviousEnvironmentApprovedCanBeSkipped":false,"enforceIdentityRevalidation":false,"timeoutInMinutes":0,"executionOrder":2}},"deployPhases":[{"deploymentInput":{"parallelExecution":{"parallelExecutionType":0},"agentSpecification":{"identifier":"windows-latest"},"skipArtifactsDownload":false,"artifactsDownloadInput":{"downloadInputs":[]},"queueId":27,"demands":[],"enableAccessToken":false,"timeoutInMinutes":0,"jobCancelTimeoutInMinutes":1,"condition":"succeeded()","overrideInputs":{}},"rank":1,"phaseType":1,"name":"Agent job","refName":null,"workflowTasks":[{"environment":{},"taskId":"31f040e5-e040-4336-878a-59a493355534","version":"1.*","name":"Initialize variable","refName":"","enabled":true,"alwaysRun":false,"continueOnError":false,"timeoutInMinutes":0,"retryCountOnTaskFailure":0,"definitionType":"task","overrideInputs":{},"condition":"succeeded()","inputs":{"Script":"#This simply exposes the Client parameter, required because of the way Task Groups expose variables to users of the group\n$tmp = \"$(Client)\"","ScriptArguments":""}},{"environment":{},"taskId":"78a30d98-c4a6-5ef0-b077-f3ef46c771d6","version":"0.*","name":"Upload $(Release.ReleaseName) - $(Build.BuildNumber) to project $(LcsProjectId)","refName":"","enabled":true,"alwaysRun":false,"continueOnError":false,"timeoutInMinutes":0,"retryCountOnTaskFailure":0,"definitionType":"task","overrideInputs":{},"condition":"succeeded()","inputs":{"serviceConnectionName":"efa96d8d-f51e-4081-a466-469ba3456f5f","projectId":"$(LcsProjectId)","assetType":"10","assetPath":"$(System.DefaultWorkingDirectory)/_Release to SandBox/drop/AXDeployableRuntime_$(Build.BuildNumber).zip","assetName":"$(Release.ReleaseName) - $(Build.BuildNumber)","assetDescription":"$(Release.ReleaseName) - $(Build.BuildNumber)","waitForValidation":"true"}},{"environment":{},"taskId":"31f040e5-e040-4336-878a-59a493355534","version":"1.*","name":"Get asset ID","refName":"","enabled":true,"alwaysRun":false,"continueOnError":false,"timeoutInMinutes":0,"retryCountOnTaskFailure":0,"definitionType":"task","overrideInputs":{},"condition":"succeeded()","inputs":{"Script":"$assetId= $env:LCSASSETUPLOAD_FILEASSETID\nWrite-Host(\"##vso[task.setvariable variable=AssetId;]$assetId\")","ScriptArguments":""}},{"environment":{},"taskId":"31f040e5-e040-4336-878a-59a493355534","version":"1.*","name":"Inline Powershell","refName":"","enabled":true,"alwaysRun":false,"continueOnError":false,"timeoutInMinutes":0,"retryCountOnTaskFailure":0,"definitionType":"task","overrideInputs":{},"condition":"succeeded()","inputs":{"Script":"Install-Module -Name 'MSAL.PS' -RequiredVersion '4.21.0.1' -AcceptLicense -Force","ScriptArguments":""}},{"environment":{},"taskId":"5852aa2c-30d2-517f-b766-bd37640e8a06","version":"2.*","name":"Deploy to sandbox2","refName":"","enabled":true,"alwaysRun":false,"continueOnError":false,"timeoutInMinutes":0,"retryCountOnTaskFailure":0,"definitionType":"task","overrideInputs":{},"condition":"succeeded()","inputs":{"serviceConnectionName":"efa96d8d-f51e-4081-a466-469ba3456f5f","projectId":"$(LcsProjectId)","environmentId":"$(LCSEnvironmentId)","fileAssetId":"$(assetId)","releaseName":"$(Release.ReleaseName) - $(Build.BuildNumber)","waitForCompletion":"false"}}]}],"environmentOptions":{"emailNotificationType":"OnlyOnFailure","emailRecipients":"release.environment.owner;release.creator","skipArtifactsDownload":false,"timeoutInMinutes":0,"enableAccessToken":false,"publishDeploymentStatus":true,"badgeEnabled":false,"autoLinkWorkItems":false,"pullRequestDeploymentEnabled":false},"demands":[],"conditions":[{"name":"ReleaseStarted","conditionType":1,"value":""}],"executionPolicy":{"concurrencyCount":1,"queueDepthCount":0},"schedules":[],"currentRelease":{"id":211,"url":"https://vsrm.dev.azure.com/ContosoD365/86984b62-8527-44d3-a2bc-d4c80f907df1/_apis/Release/releases/211","_links":{}},"retentionPolicy":{"daysToKeep":30,"releasesToKeep":3,"retainBuild":true},"processParameters":{},"properties":{"BoardsEnvironmentType":{"$type":"System.String","$value":"unmapped"},"LinkBoardsWorkItems":{"$type":"System.String","$value":"False"}},"preDeploymentGates":{"id":0,"gatesOptions":null,"gates":[]},"postDeploymentGates":{"id":0,"gatesOptions":null,"gates":[]},"environmentTriggers":[],"badgeUrl":"https://vsrm.dev.azure.com/ContosoD365/_apis/public/Release/badge/86984b62-8527-44d3-a2bc-d4c80f907df1/1/1"}],"artifacts":[{"sourceId":"86984b62-8527-44d3-a2bc-d4c80f907df1:3","type":"Build","alias":"_Release to SandBox","definitionReference":{"artifactSourceDefinitionUrl":{"id":"https://dev.azure.com/ContosoD365/_permalink/_build/index?collectionId=8b14f745-7fe1-47eb-ac2d-527e8b5f453f&projectId=86984b62-8527-44d3-a2bc-d4c80f907df1&definitionId=3","name":""},"defaultVersionBranch":{"id":"","name":""},"defaultVersionSpecific":{"id":"","name":""},"defaultVersionTags":{"id":"","name":""},"defaultVersionType":{"id":"latestType","name":"Latest"},"definition":{"id":"3","name":"Midnight Schedule - MS Hosted Agent Pipeline-import"},"definitions":{"id":"","name":""},"IsMultiDefinitionType":{"id":"False","name":"False"},"project":{"id":"86984b62-8527-44d3-a2bc-d4c80f907df1","name":"Dynamics 365"},"repository":{"id":"","name":""}},"isPrimary":true,"isRetained":false}],"triggers":[{"artifactAlias":"_Release to SandBox","triggerConditions":[],"triggerType":1}],"releaseNameFormat":"Release-$(rev:r)","tags":[],"properties":{"DefinitionCreationSource":{"$type":"System.String","$value":"ReleaseNew"},"IntegrateBoardsWorkItems":{"$type":"System.String","$value":"False"},"IntegrateJiraWorkItems":{"$type":"System.String","$value":"false"}},"id":1,"name":"Apply Package on SandBox","path":"\\","projectReference":null,"url":"https://vsrm.dev.azure.com/ContosoD365/86984b62-8527-44d3-a2bc-d4c80f907df1/_apis/Release/definitions/1","_links":{"self":{"href":"https://vsrm.dev.azure.com/ContosoD365/86984b62-8527-44d3-a2bc-d4c80f907df1/_apis/Release/definitions/1"},"web":{"href":"https://dev.azure.com/ContosoD365/86984b62-8527-44d3-a2bc-d4c80f907df1/_release?definitionId=1"}}}

Importe através do botão representado na imagem abaixo:


Na aba de variáveis, serão 3:




Client - é o tenant da empresa. Por exemplo, otavioanaga@contoso.com.br o tenant seria contoso

LCSEnvironmentId - é a guid do ambiente que receberá a release. Ela está disponível no LCS, na aba Manage Enviroment campo Enviroment Id

LCSProjectId - Acesse o projeto via LCS. O id do projeto é o número no final da URL.

Volte para a aba pipeline e configure o Artifacts:

É no Artifacts que referenciamos aquele pacote gerado pelo nosso pipeline. Delete o Artifact que veio com o arquivo e crie novamente através do botão Add, a configuração deve ser parecida com a imagem abaixo:



Importante: Certifique-se de que o Source Alias exibido nesta tela esteja contido na propriedade File to Upload do passo de upload contido na aba Tasks.





Uma vez configurado o Artifact, selecione o  e verifique o status da opção abaixo. Se ela estiver habilitada, então TODO build do pipeline configurado no passo anterior que ocorrer com sucesso, chamará o seu Release, então vai de você julgar se isso cabe no seu planejamento ou não:




Com tudo devidamente parametrizado, para testar basta ir até à pagina de releases no Azure Devops e selecionar Create a Release






Lembrando que só é possível implantar pacotes automaticamente em ambiente Sandbox. Em ambiente produtivo isso ainda não é possível. 

Quaisquer dúvidas é só postar na área de comentários.

domingo, 20 de março de 2022

Build via Microsoft Hosted Agent e Azure Pipelines

Neste post fugirei um pouco da parte de desenvolvimento e focarei em DevOps. Hoje falarei de forma pouco conhecida de build, mas que viabiliza (em ambientes saudáveis) zerar o custo de build com máquinas virtuais alocadas especificamente para este fim. Além da possibilidade de custo zero, outra grande vantagem deste tipo de build é a possibilidade de rodar N pipelines no Azure ao mesmo tempo (sem ter que criar N servers do tipo PaaS para isso) mas para isso você precisaria pagar para ter mais Azure Agents ou Scale Sets, ou seja, sair do custo 0, custo este que não chega nem perto do custo de storage do server convencional rs.

Assumo que estou com pouco tempo para escrever aqui, então não entrarei em muitos detalhes do processo, somente lhes darei o caminho das pedras para que vocês consigam evoluir sozinhos. Como referência utilizem este link oficial da Microsoft: Build automation that uses Microsoft-hosted agents and Azure Pipelines - Finance & Operations | Dynamics 365 | Microsoft Docs 

Este tipo de build precisa de uma Solution do Visual Studio, com projetos vazios que referenciem cada model existente na branch que sofrerá o build. Caso você tenha herdado um ambiente com models a dar com Pau, a dica é que crie um console application no visual studio que varra todos os descriptors e crie os .rnrprojs dentro da solution, É simples, porém um pouco trabalhoso, caso precise posso enviar a que fiz junto com o Alberto Suzuki (Alberto Suzuki | LinkedIn), só chamar via inbox, não é algo que eu me orgulhe para postar aqui. Esta solution deve sofrer checkin na branch que sofrerá o build, para que possa ser referenciada no Pipeline

Grosseiramente falando, este tipo de build baixará os arquivos do Core Microsoft, e os utilizará para realizar o build do código personalizado do cliente. Isso tudo com um server genérico que contenha uma versão mais recente do Visual Studio. Ao contrário do que muitos imaginam, o build do D365 F&O via Azure Pipeline, seja através do servidor convencional ou do Azure Agent, somente compila objetos de personalização portanto o tempo de compilação e empacotamento NÃO deve demorar muito (vejo ambientes compilando e empacotando em 10, 15 minutos, mas é lógico que depende do DynaCus implementado rsrs). Evitem criar muitas models, isso afeta o tempo de compilação, em um outro post posso detalhar melhor o por que, no entanto até lá, a dica que posso dar é que utilizem o log do DevOps para entender por que isso acontece.

Bom, então onde podemos localizar e disponibilizar estes arquivos para que o Pipeline consiga baixar e utilizar na compilação? Para isso você precisa acessar o nosso sistema de ciclo de vida do produto, mais conhecido como LCS. Navegue até a pasta de Shared Asset Library, aba Nuget Packages e baixe os arquivos correspondentes à versão do ambiente que quiser compilar, faça isso dentro de um servidor do Azure pois é necessária uma boa velocidade de upload que muito provavelmente sua internet local não terá:


Atualmente são 4 arquivos necessários. Utilize sempre os mais recentes. Pelo que percebi o minor version utilizado sempre será o do Platform Update. Não são liberados arquivos com quality services, a não ser que gerem um HOTFIX, como no caso da imagem.

Uma vez baixados os arquivos (como dito anteriormente, em um server no azure) é o momento de realizar o upload destes arquivos (é aqui que a sua internet local não vai aguentar o tranco) na sessão de Artifacts de seu projeto no DevOps. Para isto, faça o seguinte:

1 - Acesse a sessão de Artifacts do seu projeto no Azure DevOps, e selecione o botão Connect to Feed, aparecerão N opções de conexão, selecione a Nuget.exe. Baixe o executável do Nuget.exe através do botão Get the Tools. Copie TODO o XML da sessão Project Setup e coloque em um arquivo chamado nuget.config:





2 - Crie uma pasta e coloque os Nuget Packages que você baixou, o arquivo nuget.config criado a partir do passo acima e também coloque o nuget.exe . Tudo em uma pasta só!

3 - Vá no DevOps e gere uma Personal Key, esta deverá ser utilizada quando for solicitado o password no passo seguinte, via CMD. Lembrando que vc precisa ter permissões suficientes para subir arquivos no Artifacts, entre em contato com a sua EUquipe, to brincando, com o setor de infra para liberar os acessos necessários, na dúvida vá de admin que não tem erro!



4 - Abra o CMD, execute um CD para a pasta que você criou com todos os arquivos e rode os seguintes comandos. Durante a execução será pedido o seu endereço de email e sua senha, que é o Personal Access Token gerado no passo supracitado. O primeiro parâmetro é o nome do Feed, geralmente é o nome da Org do Azure DevOps e o segundo parâmetro é o nome do arquivo, não precisa do caminho completo pois como você executou o CD o CMD já buscará naquele diretório:

nuget.exe push -Source "NomeDoFeed" -ApiKey az microsoft.dynamics.ax.application.devalm.buildxpp.10.0.1084.80.nupkg
nuget.exe push -Source "NomeDoFeed" -ApiKey az microsoft.dynamics.ax.platform.compilerpackage.7.0.6253.76.nupkg
nuget.exe push -Source "NomeDoFeed" -ApiKey az microsoft.dynamics.ax.applicationsuite.devalm.buildxpp.10.0.1084.80.nupkg
nuget.exe push -Source "NomeDoFeed" -ApiKey az microsoft.dynamics.ax.platform.devalm.buildxpp.7.0.6253.76.nupkg

Uma vez feito o upload de todos os arquivos com sucesso, você terá um feed parecido com isso no seu DevOps:


Com os arquivos disponíveis no Azure Devops, agora é o momento de viabilizar fazer a referencia destes arquivos com a execução do seu pipeline, para isso suba na raiz de sua branch aquele arquivo nuget.config, também crie mais um arquivo chamado packages.config faça a referencia com o nome e versão dos arquivos que sofreram upload e por final suba-o também na raiz da branch que sofrerá o build. O packages.config deve respeitar o formato abaixo:

<?xml version="1.0" encoding="utf-8"?>
<packages>
    <package id="Microsoft.Dynamics.AX.Platform.DevALM.BuildXpp" version="7.0.5968.16973" targetFramework="net40" />
    <package id="Microsoft.Dynamics.AX.Application.DevALM.BuildXpp" version="10.0.793.16" targetFramework="net40" />
    <package id="Microsoft.Dynamics.AX.ApplicationSuite.DevALM.BuildXpp" version="10.0.793.16" targetFramework="net40" />
    <package id="Microsoft.Dynamics.AX.Platform.CompilerPackage" version="7.0.5968.16973" targetFramework="net40" />
</packages>


Agora crie um Pipeline utilizando o seguinte template: Dynamics365-Xpp-Samples-Tools/xpp-ci.yml at master · microsoft/Dynamics365-Xpp-Samples-Tools · GitHub

Com o pipeline criado, na aba Pipeline selecione o seguinte, é ai que você referencia o Microsoft Hosted Agent e deixa de utilizar um server convencional para utilizar um serviço gratuito (até uma hora de execução do Pipeline, depois desse tempo a conexão cai) da Microsoft:



 Deixe o Get Sources da forma abaixo:


Desabilite o passo "Add Licenses to Deployable Package" (na maioria das vezes você não utilizará isso, a não ser que você seja um ISV) 


Referencie a Solution com os Projects de CADA model existente:



Com isso, você já poderá executar o seu Pipeline (e enfrentar algum errinho ou outro rsrs). O resultado deve ser algo parecido com isso aqui:


O print acima é de um ambiente controlado, perceba que o build demorou somente 6 minutos. No entanto, caso o caos esteja implantando no seu cliente e a execução do seu Pipeline demore mais do que uma hora, considere a compra de mais horas de Microsoft Hosted Agent ou a criação de um Scale Set. Caso dinheiro não seja um problema, vá de Scale Set, por ele você pode criar uma máquina bem potente, a ponto de diminuir seu tempo de compilação e empacotamento.

O grande pulo do gato, é que você utiliza um genuíno SaaS neste tipo de build. Isso torna seu build mais escalável, confiável e te da mais flexibilidade no gerenciamento de execução dos Pipelines que estiverem sob sua jurisdição.

Bom, é basicamente isso. Espero que este post tenha ao menos dado uma luz do que é essa forma de build, que pelo que percebo ainda é pouco conhecida aqui pelo BR.

No próximo post te ensino como implantar de forma automática o pacote gerado pelo Pipeline, em sua Sandbox!



domingo, 7 de novembro de 2021

P.U 46 - Erro: Invalid algorithm specified. Ao assinar XML da NF-e

Esse erro começou a surgir após a aplicação do update 46. O core utiliza o algoritimo SHA1 (Secure Hash Algorithm 1), na assinatura do XML da NF-e, no entanto ele já não é seguro e está sendo removido dos produtos Microsoft. Por conta disso, como solução paliativa é necessário abrir uma excessão no escopo da aplicação, para que este algoritmo possa continuar sendo usado. A Microsoft já está a par deste problema. Como Hotfix, adicione a seguinte extension ao seu ambiente:


[ExtensionOf(classStr(EFDocSignXmlDocument_BR))]

internal final class EFDocSignXmlDocument_BR_Extension

{

    public str signDocument(

        KeyVaultCertificateRef _keyVaultCertificateRef,

        DigitalCertificateSubject _certificateSubject,

        XmlElement _xmlDoc,

        str _elementToBeSignedName)

    {

        System.AppContext::SetSwitch("Switch.System.Security.Cryptography.Xml.UseInsecureHashAlgorithms", true);

        System.AppContext::SetSwitch("Switch.System.Security.Cryptography.Pkcs.UseInsecureHashAlgorithms", true);


        return next signDocument(_keyVaultCertificateRef, _certificateSubject, _xmlDoc, _elementToBeSignedName);

    }

}

domingo, 28 de março de 2021

Recuperar Label através do ID no D365 F&O

Há uma certa limitação na busca pelo texto das labels Core do D365 F&O. Quando tentamos utilizar a ferramenta própria para isso no Visual Studio, ela simplesmente trava e não trás nenhum resultado. Para contornar isso utilize o job abaixo, ele retornará o texto da label conforme Id passado na URL:

/?mi=SysClassRunner&cls=GetLabelTextById&args=@SYS338946

class GetLabelTextById

{

    /// <summary>

    /// Runs the class with the specified arguments.

    /// </summary>

    /// <param name = "_args">The specified arguments.</param>

    public static void main(Args _args)

    {

        URLUtility    urlUtility = new URLUtility();

        Str           argsStr    = urlUtility.getQueryParamValue('args');

        Info(Microsoft.Dynamics.Ax.Xpp.LabelHelper::GetLabel(argsStr));

    }

}


terça-feira, 18 de junho de 2019

Prefix não é respeitado quando chamado por AxClasses

Precisei desenvolver uma integração que gera Ordens de Vendas no AX. Para isto utilizei a classe AxSalesTable, chamando o constructValidateInput. O validateInput valida as informações passadas para a classe, no momento em que for tentado executar o save na salesTable.

Abaixo, a maneira a qual o objeto foi instanciado:

axSalesTable = AxSalesTable::constructValidateInput(salesTable);

O meu problema ocorreu no momento em que os dados eram validados, na árvore do Infolog, com a execução em CIL habilitada:

Percebam que o resultado do validateFieldValue da SalesTable, ficou fora do nível correto na árvore. 

Quando executado em X++, a árvore era montada corretamente:


Como neste caso eu não tinha acesso ao validateFieldValue da classe xRecord, sobrescrevi o método na SalesTable, da seguinte forma:

public server boolean validateFieldValue(FieldName _fieldName, int _arrayIndex = 1)
{
    boolean ret;
    str infoOld;
    str infoNew;
    boolean isCLRSession;
    isCLRSession = xSession::isCLRSession();
    if (isCLRSession)
    {
        infoOld = infolog.text(Global::infologLine());
    }
    ret = super(_fieldName, _arrayIndex);

    if (isCLRSession)
    {
        infoNew = infolog.text(Global::infologLine());
        if(infoOld != infoNew)
        {
            infolog.cut(Global::infologLine(),Global::infologLine());
            Global::warning(infoNew);
        }
    }
   
    return ret;
}

Com isso o prefix voltou a ser respeitado no validateFieldValue, quando executado em CIL!

quinta-feira, 18 de outubro de 2018

Como reproduzir a criação do XML da NFe, de uma nota já enviada para a SEFAZ

Para isso basta colocar o seguinte código, no método main de uma classe com a propriedade RunOn como Server, colocar o RecId da FiscalDocument_BR correspondente no parâmetro do método find em seguida pressionar F5:

public server static void Main(Args _args)
{
    EFDocMsgStringStream_BR EFDocMsgStringStream_BR = new EFDocMsgStringStream_BR();
    EFiscalDocumentList_BR EFiscalDocumentList_BR = EFiscalDocumentList_BR::construct();
    EFDocMsgFormat_XmlSubmitV4_BR EFDocMsgFormat_XmlSubmitV4_BR = new EFDocMsgFormat_XmlSubmitV4_BR();
    EFiscalDocument_BR EFiscalDocument_BR;
    ;

    EFiscalDocument_BR = EFiscalDocument_BR::construct(FiscalDocument_BR::find(5640230734,false));
    EFiscalDocumentList_BR.add(EFiscalDocument_BR);
    EFDocMsgFormat_XmlSubmitV4_BR.write(EFDocMsgStringStream_BR, EFiscalDocumentList_BR);
   
    info(EFDocMsgStringStream_BR.toString());
}