Заметки консультанта

Шамрай Александр Владимирович

Archive for Февраль 2019

Azure DevOps Services Rest Api. 14. Создание и добавление тестовых сценариев

Posted by Shamrai Alexander на Февраль 28, 2019

<< Перейти в радел «Azure DevOps Services (TFS/VSTS) Rest Api»

Примечание: Устарело с версии 16.150.0-preview. Новые подходы описаны здесь.

Создание тестового сценария

Примечание: На момент написания статьи не существовало официальных задокументированных возможностей для создания тестовых сценариев через Rest Api. Ниже описанное о тестовых шагах получено опытным путем.

Клиент TestManagementHttpClient не содержит каких-либо методов для создания тестовых сценариев. Тестовые сценарии создаются как обычные рабочие элементы через клиент WorkItemTrackingHttpClient (см. Создание и редактирование рабочих элементов). Особенностью является формирование шагов тестирования и параметров для них.

Структура содержимого шагов тестирования в сценарии

Шаги тестирования содержатся в поле Microsoft.VSTS.TCM.Steps в следующем формате:

<steps id=»0″ last=»{LAST_STEP_ID}»>

<step type=»ActionStep» id=»{STEP_ID}»>

<parameterizedString isformatted=»true»>{ACTION_DESCRIPTION}</parameterizedString>

<parameterizedString isformatted=»true»></parameterizedString>

</step>

<step type=»ValidateStep» id=»{STEP_ID}»>

<parameterizedString isformatted=»true»>{ ACTION _DESCRIPTION}</parameterizedString>

<parameterizedString isformatted=»true»>{VALIDATION_DESCRIPTION}</parameterizedString>

</step>

</steps>

Тут можно выделить следующие атрибуты:

  1. {LAST_STEP_ID} – идентификатор последнего шага.
  2. type – тип шага. Если установлено значение ActionStep, то заполняется только содержимое для первой строки шага(parameterizedString), содержимое второй строки остается пустым. Если же установлено ValidateStep, то обе строки заполняются.
  3. {STEP_ID} – порядковый номер шага. Отсчет начинается с 2.
  4. {ACTION_DESCRIPTION} – описание действий в шаге.
  5. {VALIDATION_DESCRIPTION} – описание проверок правильного выполнения шага.

Структура содержимого параметров шагов тестирования

Если тестовый сценарий не использует локальные параметры, то этот пункт можно упустить. Описание параметров разделяется на две части:

  1. Описание набора параметров.
  2. Описание значений параметров.

Описание набора параметров

Набор параметров содержится в поле Microsoft.VSTS.TCM.Parameters в следующем виде:

<parameters>

<param name=»param1″ bind=»default»/>

<param name=»param2″ bind=»default»/>

</parameters>

Т.е. для каждого параметра в блоке parameters описывается строка <param name=»ИМЯ_ПАРАМЕТРА» bind=»default»/>.

Описание значений параметров

Набор параметров содержится в поле Microsoft.VSTS.TCM.LocalDataSource в следующем виде:

<NewDataSet>

<xs:schema id=’NewDataSet’ xmlns:xs=’http://www.w3.org/2001/XMLSchema&#8217; xmlns:msdata=’urn:schemas-microsoft-com:xml-msdata’>

    <xs:element name=’NewDataSet’ msdata:IsDataSet=’true’ msdata:Locale=»>

        <xs:complexType>

<xs:choice minOccurs=’0′ maxOccurs = ‘unbounded’>

            <xs:element name=’Table1′>

<xs:complexType>

                <xs:sequence>

                    <xs:element name=’param1′ type=’xs:string’ minOccurs=’0′ />

                    <xs:element name=’param2′ type=’xs:string’ minOccurs=’0′ />

                </xs:sequence>

            </xs:complexType>

        </xs:element>

    </xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

<Table1><param1>value1</ param1>< param2>value2</ param2></Table1>

<Table1>< param1>value3</ param1>< param2>value4</ param2></Table1>

</NewDataSet>

Т.е. это описание набора данных с его содержимым. Для каждого нового параметра необходимо в разделе <xs:sequence> добавить новую строку с описанием параметра: <xs:element name=’ИМЯ_ПАРАМЕТРА’ type=’xs:string’ minOccurs=’0′ /> .

Далее внизу в таблице <Table1> прописываются значения для каждого из параметров. Сколько новых строк будет в таблице – столько итераций тестирования будет запускать мастер выполнения тестовых сценариев.

Пример генерирования содержимого тестовых шагов и параметров находится в файле в TestStepsHelper.cs тестового решения ниже.

Создание сценарий тестирования с его применением может выглядеть следующим образом:

Dictionary<string, object> fields = new Dictionary<string, object>();
LocalStepsDefinition stepsDefinition = new LocalStepsDefinition();

stepsDefinition.AddStep(«Run Application»);

stepsDefinition.AddStep(«Enter creds @user_name @user_password»);

stepsDefinition.AddStep(«Check available functions», «Functions for: @user_role»);

LocalTestParams testParams = new LocalTestParams();

testParams.AddParam(«user_name», new
string[] { «admin», «user», «manager» });

testParams.AddParam(«user_password», new
string[] { «admin_pswrd», «user_pswrd», «manager_pswrd» });

testParams.AddParam(«user_role», new
string[] { «Administrator», «Local User», «Shop Manager» });

fields.Add(«Title», «new test case»);

fields.Add(FieldSteps, stepsDefinition.StepsDefinitionStr);

fields.Add(FieldParameters, testParams.ParamDefinitionStr);

fields.Add(FieldDataSource, testParams.ParamDataSetStr);

CreateWorkItem(TeamProjectName, «Test Case», fields);

А выполнение шагов будет отображаться так:

Рисунок 1. Выполнение теста

Добавление тестового сценария в существующий набор тестов

Тестовые сценарии могут быть добавлены только в статический набор тестов или набор тестов для требования. Для добавления используется метод AddTestCasesToSuiteAsync клиента TestManagementHttpClient с параметрами: наименование командного проекта, идентификатор плана тестирования для новых тестовых сценариев, идентификатор набора тестов и строка с идентификаторами рабочих элементов типа Сценарий тестирования.

TestSuite testSuite = TestManagementClient.GetTestSuiteByIdAsync(TeamProjectName, TestPlanId, testSuiteId, 1).Result;
if (testSuite.SuiteType == «StaticTestSuite» || testSuite.SuiteType == «RequirementTestSuite»)

TestManagementClient.AddTestCasesToSuiteAsync(TeamProjectName, TestPlanId, testSuiteId, String.Join(«,», TestCasesIds)).Wait();

else

Console.WriteLine(«The Test Suite ‘» + StaticSuitePath + «‘ is not static or requiremet»);

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/14.TFRestApiAppCreateAndAddTestCase

Posted in Microsoft, Team Foundation Server, Team Foundation Server FAQ, Visual Studio, visual studio team services | Отмечено: , , , , | Leave a Comment »

Azure DevOps Services Rest Api. 13. Создание плана тестирования и наборов тестов

Posted by Shamrai Alexander на Февраль 28, 2019

<< Перейти в радел «Azure DevOps Services (TFS/VSTS) Rest Api»

Примечание: Устарело с версии 16.150.0-preview. Новые подходы описаны здесь.

Создание плана тестирования

План тестирования создается с помощью метода CreateTestPlanAsync из клиента TestManagementHttpClient. Данный метод принимает только два параметра:

  • Объект класса PlanUpdateModel, который описывает основные параметры плана тестирования.
  • И имя командного проекта.

Класс PlanUpdateModel включает следующие атрибуты, которые интересуют нас при создании на данном этапе:

  • name – наименование нового плана тестирования
  • startDate – дата начала плана тестирования в виде строки
  • endDate – дата окончания плана тестирования в виде строки
  • area – путь области, который описывается классом ShallowReference
  • iteration – путь итерации

Все атрибуты довольно просты, кроме area. Получить данные для пути области мы можем с помощью методов, которые рассматривались в статье Управление областями и итерациями в командном проекте. Т.е. мы получаем объект класса WorkItemClassificationNode и передаем его атрибуты в новый объект класса ShallowReference:

  1. В атрибут Id передаем Identifier.
  2. В атрибут Url передаем соответствующий Url области.

Пример создания плана тестирования:

Microsoft.TeamFoundation.TestManagement.WebApi.ShallowReference areaRef = null;
if (AreaPath != «»)

{


var area = WitClient.GetClassificationNodeAsync(TeamProjectName, TreeStructureGroup.Areas, AreaPath).Result;

areaRef = new Microsoft.TeamFoundation.TestManagement.WebApi.ShallowReference() {

Id = area.Identifier.ToString(),

Name = TeamProjectName + «\\» + AreaPath,

Url = area.Url

};

}

if (IterationPath != «») IterationPath = TeamProjectName + «\\» + IterationPath;

PlanUpdateModel newPlanDef = new PlanUpdateModel(

name: TestPlanName,

startDate: (StartDate.HasValue) ? StartDate.Value.ToString(«o») : «»,

endDate: (FinishDate.HasValue) ? FinishDate.Value.ToString(«o») : «»,

area: areaRef,

iteration: IterationPath

);

TestManagementClient.CreateTestPlanAsync(newPlanDef, TeamProjectName).Wait();

Создание тестового набора

Создание тестового набора выполняется схожим методом с созданием плана тестирования, т.е. через отдельный объект, который описывает свойства нового набора тестов. Этот объект описывается классом SuiteCreateModel, который включает атрибуты:

  1. suiteType – тип набор тестирования, который может быть одним из:
    1. StaticTestSuite – статический набор тестов, который может включать дочерние наборы тестов и тестовые сценарии.
    2. DynamicTestSuite – динамический набор тестов, который содержит тесты, отобранные его запросом через атрибут queryString.
    3. RequirementTestSuite – набор тестов, который ассоциируется с рабочим элементов группы требований и может содержать только тестовые сценарии.
  2. name – наименование нового набора тестов.
  3. queryString – WIQL запрос для отбора тестов для динамического набора тестов.
  4. requirementIds – массив идентификаторов рабочих элементов группы требований, для которых будут созданы тестовые наборы с типом RequirementTestSuite.

Создание тестового набора выполняется с помощью метода CreateTestSuiteAsync, в который передаются:

  1. Экземпляр класса SuiteCreateModel.
  2. Наименование командного проекта.
  3. Идентификатор плана тестирования, в котором будет создан новый набор тестов.
  4. Идентификатор родительского набора тестов, который должен быть статическим. Если создаем в корне плана тестирования, то такой идентификатор можно найти в его атрибуте RootSuite.Id.

Прим создания набора тестов:

SuiteCreateModel newSuite = new SuiteCreateModel(TestSuiteType, TestSuiteName, SuiteQuery, RequirementIds);
List<TestSuite> testSuiteList = TestManagementClient.CreateTestSuiteAsync(newSuite, TeamProjectName, TestPlanId, parentsuiteId).Result;

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/13.TFRestApiAppCreateTestPlanAndSuites

Posted in Microsoft, Team Foundation Server, Team Foundation Server FAQ, Visual Studio, visual studio team services | Отмечено: , , , , | Leave a Comment »

Автоматизация изменений состояний рабочих элементов Azure DevOps на основе состояний дочерних элементов

Posted by Shamrai Alexander на Февраль 19, 2019

На форумах часто встречается вопросы:

  1. Есть ли какой-то сервис для автоматического изменения состояния пользовательской истории, если отрыли любую дочернюю задачу.
  2. Как можно автоматически закрыть требование, если все дочерние задачи закрыты.

На сколько верно автоматизировать эти вопросы – это тема дискуссионная, и каждый сам для себя выбирает правильный ответ. Если все-таки есть желание автоматизировать изменение состояний, то для этого существуют отдельные сервисы, например, TFS Aggregator (Web Service) или другие кастомные сервисы.

Но т.к. задачи на самом деле с точки зрения разработки не очень сложные, то содержание отдельного сервиса может быть избыточным. Иногда можно обойтись небольшими задачами на основе библиотек для взаимодействия через REST API, которые будут выполняться через стандартный планировщик задач. Такой пример мы здесь и рассмотрим.

Тут будут следующие основные шаги:

  1. Подключение к сервису. Пример есть здесь: VSTS Rest Api. 1. Подключение к сервису.
  2. Выполнения запроса по рабочим элементам, чтобы иметь список интересующих нас «родителей». Пример выполнения запроса: Azure DevOps Services Rest Api. 4. Выполнение запросов по рабочим элементам
  3. Обновить состояние «родителям», которые удовлетворяют нашим условиям. Пример обновления: Azure DevOps Services Rest Api. 3. Создание и редактирование рабочих элементов

Изменение родителя, если любой дочерний элемент изменился

Примеры тут могут быть следующие:

  1. Открывается пользовательская история, если исполнитель отрыл дочернюю задачу.
  2. Открывается фича, если любая дочерняя пользовательская история открылась.

Это простой сценарий, т.к. нам нужно сделать запрос по рабочим элементам, который отберет пользовательские истории в состоянии New и с дочерними задачами в состоянии Active. Пример запроса в формате WIQL может быть следующий:

SELECT [System.Id] FROM WorkItemLinks WHERE ([Source].[System.TeamProject] = ‘Team project name’ AND [Source].[System.WorkItemType] = ‘User story’ AND [Source].[System.State] = ‘New’) And ([System.Links.LinkType] = ‘System.LinkTypes.Hierarchy-Forward’) And ([Target].[System.WorkItemType] = ‘Task’ AND [Target].[System.State] = ‘Active’) ORDER BY [System.Id] mode(MustContain)

Нас интересуют только идентификаторы рабочих элементов верхнего, которые в результате выполнения запроса не имеют участника Source. Пример обработки результата:

static List<int> GetParentIdsFromQueryResult(string pWiql)

{

Wiql _wiql = new Wiql { Query = pWiql };

WorkItemQueryResult result = WITClient.QueryByWiqlAsync(_wiql).Result;

if (result.WorkItemRelations != null)

return (from links in result.WorkItemRelations where links.Source == null select links.Target.Id).ToList();


return new List<int>();

}

Далее все найденные идентификаторы обновляем до активного состояния. При этом функция обновления проста, т.к. нам нужно обновить только одно поле:


static void UpdateWorkItemState(int parentId, string pDestinationState)

{

JsonPatchDocument patchDocument = new JsonPatchDocument();

patchDocument.Add(new JsonPatchOperation()

{

Operation = Operation.Add,

Path = «/fields/» + StateFieldName,

Value = pDestinationState

});

WorkItem _updatedWi = WITClient.UpdateWorkItemAsync(patchDocument, parentId).Result;

Console.WriteLine(«Work Item Has Been Updated:{0} — {1}», _updatedWi.Id, _updatedWi.Fields[«System.State»].ToString());

}

Изменение родителя, если все дочерние элементы в необходимом состоянии

Тут рассматривается вариант, если необходимо закрыть пользовательскую историю, когда все дочерние задачи закрыты.

В данном случае получение необходимых родителей схож с первым вариантом. Но нам необходимо знать: все ли дочерние задачи закрыты. Это одним запросом не выполнишь. Можно использовать два варианта:

  1. Получать родителя со всеми ссылками на дочерние элементы. Потом получать дочерние задачи, анализировать их состояние и принимать необходимое решение.
  2. Также можно выполнить два запроса для каждого родителя: количество всех дочерних задач и количество закрытых дочерних задач. Если они равны, то закрываем пользовательскую историю. Рассмотрим этот вариант.

Запросы для родительских пользовательских историй:

  1. Получить родителя и закрытые дочерние задачи:

SELECT[System.Id] FROM WorkItemLinks WHERE([Source].[System.TeamProject] = ‘Team project name’ AND [Source].[System.Id] = Parent_ID) And([System.Links.LinkType] = ‘System.LinkTypes.Hierarchy-Forward’) And ([Target].[System.WorkItemType] = ‘Task’ AND [Target].[System.State] = ‘Closed’) ORDER BY [System.Id] mode(MustContain)

  1. Получить родителя и все дочерние задачи

SELECT[System.Id] FROM WorkItemLinks WHERE([Source].[System.TeamProject] = ‘Team project name’ AND [Source].[System.Id] = Parent_ID) And ([System.Links.LinkType] = ‘System.LinkTypes.Hierarchy-Forward’) And ([Target].[System.WorkItemType] = ‘Task’) ORDER BY [System.Id] mode(MustContain)

Получить количество дочерних рабочих элементов из запроса можно следующим образом:


static int GetChildsCountFromQueryResult(string pWiql)

{

Wiql _wiql = new Wiql { Query = pWiql };

WorkItemQueryResult result = WITClient.QueryByWiqlAsync(_wiql).Result;


if (result.WorkItemRelations != null)


return (from links in result.WorkItemRelations where links.Source != null select links.Target.Id).Count();


return 0;

}

Затем сравниваем количество и обновляем тем же методом, как и в первом варианте.

Решение пример

Решение можно получить и скомпилировать отсюда: https://github.com/ashamrai/AzureDevOpsExtensions/tree/master/CustomNetTasks/UpdateParentState

Перед запуском приложения необходимо:

  1. Установить правильный адрес Azure DevOps сервиса или сервера в константу ServiceUrl.
  2. Сгенерировать Personal Access Token и вставить в константу PAT.
  3. Добавить в список _projects названия командных проектов для обработки: _projects.Add(«Yor team project name»);

Posted in Microsoft, Team Foundation Server, Visual Studio, visual studio team services | Отмечено: , , , , , | Leave a Comment »

Azure DevOps Services Rest Api. 12. Просмотр содержимого плана тестирования

Posted by Shamrai Alexander на Февраль 14, 2019

<< Перейти в радел «Azure DevOps Services (TFS/VSTS) Rest Api»

Примечание: Устарело с версии 16.150.0-preview. Новые подходы описаны здесь.

Для работы с объектами тестирования используется клиент TestManagementHttpClient. В рамках планирования тестирования используются следующие основные объекты:

  1. План тестирования – класс TestPlan. Он содержит в основном информацию о названии, планируемых датах тестирования и т.д.
  2. Набор тестов – класс TestSuite. Содержит дочерние наборы, а также сценарии тестирования. Наборы тестов могут быть статическими, наборам для требований (т.е. ссылаются на конкретное требование) и динамическими (на основе запроса по рабочим элементам).
  3. Тестовый сценарий – класс SuiteTestCase. Содержит информацию о рабочем элементе тестового сценария, тестировщике и конфигурации для тестирования.

Для того, чтобы получить информацию о всех объектах тестирования, необходимо:

  1. Получить план тестирования.
  2. Получить его основной набор тестов. Т.к. план тестирования кроме того, что имеет объект, который описывает его свойства, для него также сразу создается корневой набор тестов, который уже отражает содержимое плана тестирования на первом уровне.
  3. Получить содержимое набора тестов, т.е. дочерние наборы тестов и тестовые сценарии.

Для получения тестового плана используется метод GetPlanByIdAsync с использованием имени командного проекта и идентификатора. Идентификатор можно увидеть через интерфейс:

Рисунок 1. Редактирование плана тестирование

Рисунок 2. Идентификатор плана тестирования

Набор планов тестирования с использованием имени командного проекта можно получить через метод GetPlansAsync, который вернет список List планов тестирования.

Пример получения плана тестирования и его свойств:

TestPlan testPlan = TestManagementClient.GetPlanByIdAsync(TeamProjectName, TestPlanId).Result;
Console.WriteLine(«Test Plan : {0} : {1} : {2}», testPlan.Id, testPlan.State, testPlan.Name);
Console.WriteLine(«Area Path : {0} : Iteration Path : {1}», testPlan.Area.Name, testPlan.Iteration);

Console.WriteLine(«Plan Dates : {0} — {1}», testPlan.StartDate.ToShortDateString(), testPlan.EndDate.ToShortDateString());

Набор тестов можно получить через метод GetTestSuiteByIdAsync, который использует наименование проекта, идентификатор плана тестирования и идентификатор набора тесто, а также признак необходимости получения информации о дочерних объектах. Корневой набор тестов можно получить из свойств плана тестирования testPlan.RootSuite.Id. При этом существует метод GetTestSuitesForPlanAsync, который позволяет получить наборы тестов без необходимости получения корневого набора, но в рамках примера для обеспечения единого подхода, он не используется. Пример получения набора тестов:

TestSuite suiteDetail = TestManagementClient.GetTestSuiteByIdAsync(TeamProjectName, PlanId, suiteId, 1).Result;
Console.WriteLine(«Test Suite : {0} : {1} : {2}», suiteDetail.Id, suiteDetail.State, suiteDetail.Name);
//Sute Types: StaticTestSuite, RequirementTestSuite, DynamicTestSuite

Console.WriteLine(«Suite Type : {0} : {1}», suiteDetail.SuiteType,

(suiteDetail.SuiteType == «StaticTestSuite») ? «» :

(suiteDetail.SuiteType == «DynamicTestSuite») ? «\nQuery: « + suiteDetail.QueryString : «Requirement ID « + suiteDetail.RequirementId.ToString());

if (suiteDetail.Parent == null) Console.WriteLine(«This is a root suite»);

Информация о дочерних наборах тестов находится в suiteDetail.Suites, а информация о наличии тестовых сценариев в suiteDetail.TestCaseCount.

Список тестов в наборе тестов можно получить через метод GetTestCasesAsync, который использует наименование проекта, идентификатор плана тестирования и идентификатор набора тестирования. Пример получения списка тестов и их рабочих элементов:

List<SuiteTestCase> testCases = TestManagementClient.GetTestCasesAsync(TeamProjectName, PlanId, suiteId).Result;
foreach(SuiteTestCase testCase in testCases)
{

int testId = 0;

if (!int.TryParse(testCase.Workitem.Id, out testId)) continue;


WorkItem wi = WitClient.GetWorkItemAsync(testId).Result;


Console.WriteLine(«Test: {0} — {1}», wi.Id, wi.Fields[«System.Title»].ToString());

foreach(var config in testCase.PointAssignments)


Console.WriteLine(«Run for: {0} : {1}», config.Tester.DisplayName, config.Configuration.Name);

}

Пример тестового приложения можно посмотреть здесь:

https://github.com/ashamrai/TFRestApi/tree/master/12.TFRestApiAppTestPlanDelails

Posted in Microsoft, Team Foundation Server, Visual Studio, visual studio team services | Отмечено: , , , , , | Leave a Comment »

Создание собственной задачи сборки и релиза TFS для регистрации новых рабочих элементов на основе Node.JS

Posted by Shamrai Alexander на Февраль 14, 2019

Введение

На сегодняшний день доступно довольно много различных как стандартных, так и сторонних задач для шагов автоматизации сборки и развертывания релиза. Но иногда возникают потребности, которые не покрываются существующими решениями. Например, для релизов существуют некоторые встроенные возможности для утверждений развертывания и т.д., но иногда их не хватает. Т.е., если необходимо релиз «пропустить» через несколько отделов (тестирование, поддержка, безопасность), то требуется более чем один утверждающий. Это можно решить, например, динамическим созданием проверочных задач для соответствующих ответственных в зависимости от среды, где разворачивается релиз.

В рамках данного поста мы рассмотрим пример создания одного рабочего элемента для демонстрации возможностей. Данный пример может быть доработан для создания группы связанных задач.

Исходный код решения

Полный пример исходного кода решения находится здесь: https://github.com/ashamrai/AzureDevOpsExtensions/tree/master/CustomBuildTask

Решение подготовлено с помощью Visual Studio Code.

Предварительные настройки

В качестве подготовки рекомендуется выполнить следующие шаги:

  1. Развернуть пакет TFS Cross Platform Command Line Interface через команду npm i -g tfx-cli.
  2. Создать каталог, в котором будет находится все решение (Каталог решения).
  3. Создать каталог, в котором будет находится исходный код для создания рабочего элемента (Каталог исходного кода).
  4. Перейти в каталог исходного кода и выполнить следующие действия:
    1. Выполнить команду npm init, которая создаст файл package.json.
    2. Добавить библиотеки azure-pipelines-task-lib через команду npm install azure-pipelines-task-lib –save.
    3. Добавить внешние библиотеки через команды npm install @types/node —save-dev и npm install @types/q —save-dev.
    4. Добавить решение для создания запросов к сервису через команду npm install xmlhttprequest –save.
    5. Добавить решение для кодирования личного ключа доступа к сервису npm install buffer —save.
    6. Создать файл настройки компиляции tsconfig.json через команду tsc –init. В данном файле устанавливаем целевую версию typescript «target»: «es6».

Создание расширения

Файл описания задачи

Перед написанием основного исходного кода, необходимо сформировать файл описания задачи для определения сборки или релиза. Для этого необходимо создать файл task.json, который описывает задачу и ее параметры. Содержимое файла приблизительно следующее:

{

«id»: «c6ff84bf-0260-47d6-889d-6c2ecbbe2aa5»,

«name»: «CreateWorkItemCustomTask»,

«friendlyName»: «Create New Work Item Custom Task»,

«description»: «Create a new work item and link it to a new build or release»,

«helpMarkDown»: «»,

«category»: «Utility»,

«author»: «Aleksandr Shamrai»,

«version»: {

«Major»: 0,

«Minor»: 1,

«Patch»: 32

},

«instanceNameFormat»: «New Work Item $(workitemtype) $(titletemplate)»,

«inputs»: [

{

«name»: «workitemtype»,

«type»: «pickList»,

«label»: «Worm Item Type»,

«defaultValue»: «Task»,

«required»: true,

«helpMarkDown»: «Select the type for new work item»,

«options»: {

«Task»: «Task»,

«Bug»: «Bug»

}

},

{

«name»: «titletemplate»,

«type»: «string»,

«label»: «Title Template»,

«defaultValue»: «»,

«required»: false,

«helpMarkDown»: «A title template»

},

{

«name»: «assignedto»,

«type»: «string»,

«label»: «Assigne to»,

«defaultValue»: «»,

«required»: false,

«helpMarkDown»: «Set an owner of a work item»

},

{

«name»: «linktobuild»,

«type»: «radio»,

«label»: «Link New Work Item to Build»,

«defaultValue»: «No»,

«required»: true,

«helpMarkDown»: «Link a new work item to build»,

«options»: {

«No»: «No»,

«LinkAsArtifact»: «Link as artifact (only for builds)»,

«LinkAsWebUrl»: «Link as Web Url»

}

}

],

«execution»: {

«Node»: {

«target»: «index.js»

}

}

}

Полное описание формата файла можно найти здесь: https://github.com/Microsoft/azure-pipelines-task-lib/blob/master/tasks.schema.json

В нашем случае основные пункты:

  1. Id – уникальный идентификатор
  2. Name – наименование задачи без пробелов.
  3. friendlyName – описательное наименование задачи
  4. category – категория, в которой мы найдем нашу задачу
  5. author – автор задачи
  6. instanceNameFormat – как данный шаг будет отражаться в списке задач сборки или релиза.
  7. Inputs – список входных параметров для задачи. Каждый параметр имеет следующие основные элементы:
    1. Name – наименование параметра
    2. Type – тип параметра (boolean, filePath, multiLine, pickList, radio, secureFile, string).
    3. Label – название параметра на форме задачи
  8. Execution – описание того, что нужно выполнять. В данном случае будет выполняться файл index.js, который создается ниже.

Исполняемый код задачи

Создадим файл index.ts, который будет содержать основной код. В данном файле можно выделить следующие моменты:

  • Как основная запускаемая функция используется функция run, которая детализируется под конкретные задачи.
  • Для получения входных параметров задачи, которые описаны в «task.json», используется метод getInput с передачей наименования параметра.
  • Для получения переменных окружения используется метод getVariable. Все переменные окружения, которые присутствуют при выполнении задачи можно посмотреть здесь: Default and custom release variables and debugging. При этом в рамках задачи используются следующие важные переменные:
    • System.AccessToken – содержит персональный ключ доступа к сервису. При этом доступ к нему обеспечивается отдельно в самом определении сборки или релиза в параметре Allow scripts to access the OAuth token. Если данный параметр не будет выставлен, то авторизация будет невозможна.
    • System.TeamFoundationCollectionUri – адрес сервера TFS.
    • System.TeamProject – командный проект, в котором выполняется сборки или развертывается релиз.
    • И т.д.

Пример получение значения параметров и переменных окружения:

const inputWIType: string = tl.getInput(‘workitemtype’, true);

const inputTitle: string = tl.getInput(‘titletemplate’, false);

const inputAssignedTo: string = tl.getInput(‘assignedto’, false);

const inputLinkToBuild: string = tl.getInput(‘linktobuild’, true);

const strAC = tl.getVariable(‘System.AccessToken’);

if (strAC == null)

{

tl.setResult(tl.TaskResult.Failed, ‘Access to OAuth token is not allowed’);

return;

}

const strServiceUrl = tl.getVariable(‘System.TeamFoundationCollectionUri’);

const strTeamProjectName = tl.getVariable(‘System.TeamProject’);

  • Создание рабочего элемента выполняется на основе запроса Rest Api Work Items – Create. Код для выполнения запроса следующий:
let devRequest = new XMLHttpRequest();

let encodedData = new Buffer(«:» + strAC).toString(‘base64’);

devRequest.open(«POST», strServiceUrl + strTeamProjectName + urlCreateTask)

devRequest.setRequestHeader(‘Authorization’, ‘Basic ‘ + encodedData);

devRequest.setRequestHeader(‘Accept’, ‘application/json’);

devRequest.setRequestHeader(‘Content-Type’, ‘application/json-patch+json’);

devRequest.send(JSON.stringify(patchFields));

devRequest.onreadystatechange = function () {

if (devRequest.readyState === 4) {

let newWorkItem = JSON.parse(devRequest.responseText);

if («id» in newWorkItem)

console.log(«The work item has been created», newWorkItem.id);

else

console.log(‘Error’,devRequest.responseText);

}

}

В данном примере создается запрос на основе авторизации Personal Access Token с использования адреса для создания нового рабочего элемента:

POST http://{server_name}:{port}/{collection name}/{project}/_apis/wit/workitems/${type}?api-version=2.0

В тело запроса в формате JSON добавляется список создаваемых полей и связей с другими объектами:

  • Для указания содержимого в поле применяется формат:
[

{

«op»: «add»,

«path»: «/fields/System.Title»,

«from»: null,

«value»: «Sample task»

}

]

  • Для создания новой гиперссылки используется формат:
[

{

«op»: «add»,

«path»: «/relations/-«,

«value»: {

«rel»: «Hyperlink»,

«url»: «URL»

}

}

]

  • Для создания ссылки на сборку используется следующий формат:
[

{

«op»: «add»,

«path»: «/relations/-«,

«value»: {

«rel»: «ArtifactLink»,

«url»: «artifact URI»,

«attributes»: {«name»:»Build»}

}

}

]

Компиляция решения

Для формирования файла index.js необходимо выполнить в каталоге исходного кода команду tsc.

Файл манифест расширения

Манифест расширения описывает расширение для TFS и создается через файл vss-extension.json. Файл содержит следующее:

{

«manifestVersion»: 1,

«id»: «new-work-item-custom-task»,

«name»: «Create New Work Item Custom Task»,

«version»: «0.1.32»,

«publisher»: «AShamrai»,

«targets»: [

{

«id»: «Microsoft.TeamFoundation.Server»

}

],

«description»: «Create a new work item and link it a build or release»,

«content»: {

«details»: {

«path»: «details.md»

}

},

«categories»: [

«Build and release»

],

«icons»: {

«default»: «images/newworkitem.png»

},

«files»: [

{

«path»: «NewWICustomTask»

}

],

«contributions»: [

{

«id»: «mew-work-tem-custom-build-release-task»,

«type»: «ms.vss-distributed-task.task»,

«targets»: [

«ms.vss-distributed-task.tasks»

],

«properties»: {

«name»: «NewWICustomTask»

}

}

]

}

Описание важных пунктов:

  • Id – уникальный идентификатор задачи
  • Targets:id – целевая платформа
  • Contributions:Id – уникальный идентификатор задачи
  • Сontributions :type – тип задачи
  • Contributions:targets – целевые задачи, в которых будет участвовать расширение.
  • Contributions:properties:name – наименование задачи, которое совпадает с каталогом, котором находятся скомпилированные исходники.
  • Files: path – путь к каталогу с содержимым задачи.

Полная спецификация содержимого находится по этому адресу: Extension manifest reference.

Сборка решения

Для сборки решения необходимо из каталога решения выполнить команду:

tfx extension create —manifest-globs vss-extension.json

Установка

Для развертывания решения можно выполнить следующие шаги:

  • Перейти на страницу управления расширениями Team Foundation Server. Она обычно находится по адресу http://server_name:8080/tfs/_gallery/manage. На этой странице необходимо нажать кнопку Upload new extension, найти сформированный после сборки решения файл расширения и выполнить его установку. Эта операция установит решение в сервер.

Рисунок 1. Страница управления расширениями

  • После того как появилось решение в списке, мы нажимаем на его ссылку и выполняем установку на необходимую коллекцию проектов.

Рисунок 2. Развертывание решения в необходимой коллекции

  • После развертывания решения мы можем приступить к созданию необходимого определения сборки или релиза.

Рисунок 3. Новая задача в списке утилит

  • При этом нужно помнить, что для того, чтобы решение могло взаимодействовать с сервером, необходимо дать ему возможность получить персональный код доступа, который используется агентом сборки для доступа к серверу.

Рисунок 4. Доступ к персональному ключу доступа

  • Результат можно увидеть в логах выполнения сборки или релиза, а также в виде созданного рабочего элемента.

Рисунок 5. Лог работы задачи

Рисунок 6. Новый рабочий элемент, который ссылается на сборку

Posted in Microsoft, Team Foundation Server, Visual Studio | Отмечено: , , , | Leave a Comment »

 
%d такие блоггеры, как: