├── .gitignore ├── publish.sh ├── icon.png ├── .vscode ├── extensions.json ├── tasks.json ├── settings.json └── launch.json ├── .vscodeignore ├── .eslintrc.json ├── src ├── test │ ├── suite │ │ ├── extension.test.ts │ │ └── index.ts │ └── runTest.ts ├── generators │ ├── ios-generator.ts │ ├── android-generator.ts │ ├── xamarin-ios-generator.ts │ ├── xamarin-forms-generator.ts │ ├── xamarin-android-generator.ts │ ├── yaml-generator.ts │ └── steps.ts ├── helpers │ ├── file-helper.ts │ └── multi-step-case.ts └── extension.ts ├── CHANGELOG.md ├── tsconfig.json ├── webpack.config.js ├── LICENSE.md ├── dist ├── extension.js.LICENSE.txt └── extension.js ├── README.md ├── package.json └── templates ├── android.stages.yml.tmpl ├── ios.stages.yml.tmpl ├── xamarin.ios.stages.yml.tmpl ├── xamarin.android.stages.yml.tmpl └── xamarin.forms.stages.yml.tmpl /.gitignore: -------------------------------------------------------------------------------- 1 | out 2 | node_modules 3 | .vscode-test/ 4 | *.vsix 5 | -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | npm install && 2 | npm run test-compile && 3 | vsce package -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damienaicheh/azure-devops-yaml-generator/HEAD/icon.png -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | out/test/** 4 | src/** 5 | .gitignore 6 | vsc-extension-quickstart.md 7 | **/tsconfig.json 8 | **/.eslintrc.json 9 | **/*.map 10 | **/*.ts 11 | 12 | # Extra file to ignore 13 | vsc-extension-quickstart.md 14 | node_modules/**/test/** 15 | tsconfig.json 16 | .eslintrc 17 | node_modules 18 | out/ 19 | src/ 20 | webpack.config.json -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "parserOptions": { 5 | "ecmaVersion": 6, 6 | "sourceType": "module" 7 | }, 8 | "plugins": [ 9 | "@typescript-eslint" 10 | ], 11 | "rules": { 12 | "@typescript-eslint/class-name-casing": "warn", 13 | "@typescript-eslint/semi": "warn", 14 | "curly": "warn", 15 | "eqeqeq": "warn", 16 | "no-throw-literal": "warn", 17 | "semi": "off" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test/suite/extension.test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'assert'; 2 | 3 | // You can import and use all API from the 'vscode' module 4 | // as well as import your extension to test it 5 | import * as vscode from 'vscode'; 6 | // import * as myExtension from '../extension'; 7 | 8 | suite('Extension Test Suite', () => { 9 | vscode.window.showInformationMessage('Start all tests.'); 10 | 11 | test('Sample test', () => { 12 | assert.equal(-1, [1, 2, 3].indexOf(5)); 13 | assert.equal(-1, [1, 2, 3].indexOf(0)); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ### 1.0.1 4 | 5 | Update all packages to fix dependencies with new security issues. 6 | 7 | ### 1.0.0 8 | Stable version. 9 | 10 | Supports added for: 11 | - iOS templates 12 | - Android templates 13 | 14 | Fix issues: 15 | - Back button increase the current steps number. 16 | - A folder project was needed to display the file generated. 17 | 18 | ### 0.0.3 19 | Supports added for: 20 | - Xamarin.iOS templates 21 | - Xamarin.Android templates 22 | 23 | Possibility to choose to generate an Android App Bundle or an Apk 24 | 25 | ### 0.0.2 26 | Fix path version for Windows. 27 | 28 | ### 0.0.1 29 | 30 | Demo version: 31 | - Xamarin.Forms templates -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "outDir": "out", 6 | "lib": [ 7 | "es6" 8 | ], 9 | "sourceMap": true, 10 | "rootDir": "src", 11 | "strict": true /* enable all strict type-checking options */ 12 | /* Additional Checks */ 13 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 14 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 15 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 16 | }, 17 | "exclude": [ 18 | "node_modules", 19 | ".vscode-test" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/generators/ios-generator.ts: -------------------------------------------------------------------------------- 1 | import { YamlGenerator } from "./yaml-generator"; 2 | import * as steps from "./steps"; 3 | 4 | export class IOSGenerator extends YamlGenerator { 5 | 6 | constructor() { 7 | super(); 8 | this.template = 'ios.stages.yml.tmpl'; 9 | this.vmImage = 'macOS-latest'; 10 | this.steps = this.steps.concat([ 11 | input => steps.manageVersionAutomatically(this, input), 12 | input => steps.updateIdentifier(this, input), 13 | input => steps.addLaunchIconBadge(this, input), 14 | input => steps.publishArtifacts(this, input), 15 | input => steps.enableAppCenterDistribution(this, input), 16 | ]); 17 | } 18 | } -------------------------------------------------------------------------------- /src/generators/android-generator.ts: -------------------------------------------------------------------------------- 1 | import { YamlGenerator } from "./yaml-generator"; 2 | import * as steps from "./steps"; 3 | 4 | export class AndroidGenerator extends YamlGenerator { 5 | 6 | constructor() { 7 | super(); 8 | this.template = 'android.stages.yml.tmpl'; 9 | this.vmImage = 'macOS-latest'; 10 | this.steps = this.steps.concat([ 11 | input => steps.manageVersionAutomatically(this, input), 12 | input => steps.updateIdentifier(this, input), 13 | input => steps.addLaunchIconBadge(this, input), 14 | input => steps.publishArtifacts(this, input), 15 | input => steps.enableAppCenterDistribution(this, input), 16 | ]); 17 | } 18 | } -------------------------------------------------------------------------------- /src/test/runTest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | 3 | import { runTests } from 'vscode-test'; 4 | 5 | async function main() { 6 | try { 7 | // The folder containing the Extension Manifest package.json 8 | // Passed to `--extensionDevelopmentPath` 9 | const extensionDevelopmentPath = path.resolve(__dirname, '../../'); 10 | 11 | // The path to test runner 12 | // Passed to --extensionTestsPath 13 | const extensionTestsPath = path.resolve(__dirname, './suite/index'); 14 | 15 | // Download VS Code, unzip it and run the integration test 16 | await runTests({ extensionDevelopmentPath, extensionTestsPath }); 17 | } catch (err) { 18 | console.error('Failed to run tests'); 19 | process.exit(1); 20 | } 21 | } 22 | 23 | main(); 24 | -------------------------------------------------------------------------------- /src/generators/xamarin-ios-generator.ts: -------------------------------------------------------------------------------- 1 | import { YamlGenerator } from "./yaml-generator"; 2 | import * as steps from "./steps"; 3 | 4 | export class XamariniOSGenerator extends YamlGenerator { 5 | 6 | constructor() { 7 | super(); 8 | this.template = 'xamarin.ios.stages.yml.tmpl'; 9 | this.vmImage = 'macOS-latest'; 10 | this.steps = this.steps.concat([ 11 | input => steps.enableUnitTests(this, input), 12 | input => steps.manageVersionAutomatically(this, input), 13 | input => steps.updateIdentifier(this, input), 14 | input => steps.addLaunchIconBadge(this, input), 15 | input => steps.publishArtifacts(this, input), 16 | input => steps.enableAppCenterDistribution(this, input), 17 | ]); 18 | } 19 | } -------------------------------------------------------------------------------- /src/generators/xamarin-forms-generator.ts: -------------------------------------------------------------------------------- 1 | import { YamlGenerator } from "./yaml-generator"; 2 | import * as steps from "./steps"; 3 | 4 | export class XamarinFormsGenerator extends YamlGenerator { 5 | 6 | constructor() { 7 | super(); 8 | this.template = 'xamarin.forms.stages.yml.tmpl'; 9 | this.vmImage = 'macOS-latest'; 10 | this.steps = this.steps.concat([ 11 | input => steps.enableUnitTests(this, input), 12 | input => steps.androidPackageType(this, input), 13 | input => steps.manageVersionAutomatically(this, input), 14 | input => steps.updateIdentifier(this, input), 15 | input => steps.addLaunchIconBadge(this, input), 16 | input => steps.publishArtifacts(this, input), 17 | input => steps.enableAppCenterDistribution(this, input), 18 | ]); 19 | } 20 | } -------------------------------------------------------------------------------- /src/generators/xamarin-android-generator.ts: -------------------------------------------------------------------------------- 1 | import { YamlGenerator } from "./yaml-generator"; 2 | import * as steps from "./steps"; 3 | 4 | export class XamarinAndroidGenerator extends YamlGenerator { 5 | 6 | constructor() { 7 | super(); 8 | this.template = 'xamarin.android.stages.yml.tmpl'; 9 | this.vmImage = 'macOS-latest'; 10 | this.steps = this.steps.concat([ 11 | input => steps.enableUnitTests(this, input), 12 | input => steps.androidPackageType(this, input), 13 | input => steps.manageVersionAutomatically(this, input), 14 | input => steps.updateIdentifier(this, input), 15 | input => steps.addLaunchIconBadge(this, input), 16 | input => steps.publishArtifacts(this, input), 17 | input => steps.enableAppCenterDistribution(this, input), 18 | ]); 19 | } 20 | } -------------------------------------------------------------------------------- /src/test/suite/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as Mocha from 'mocha'; 3 | import * as glob from 'glob'; 4 | 5 | export function run(): Promise { 6 | // Create the mocha test 7 | const mocha = new Mocha({ 8 | ui: 'tdd', 9 | color: true 10 | }); 11 | 12 | const testsRoot = path.resolve(__dirname, '..'); 13 | 14 | return new Promise((c, e) => { 15 | glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { 16 | if (err) { 17 | return e(err); 18 | } 19 | 20 | // Add files to the test suite 21 | files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); 22 | 23 | try { 24 | // Run the mocha test 25 | mocha.run(failures => { 26 | if (failures > 0) { 27 | e(new Error(`${failures} tests failed.`)); 28 | } else { 29 | c(); 30 | } 31 | }); 32 | } catch (err) { 33 | console.error(err); 34 | e(err); 35 | } 36 | }); 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | 'use strict'; 4 | 5 | const path = require('path'); 6 | 7 | /**@type {import('webpack').Configuration}*/ 8 | const config = { 9 | target: 'node', 10 | 11 | entry: './src/extension.ts', 12 | output: { 13 | path: path.resolve(__dirname, 'dist'), 14 | filename: 'extension.js', 15 | libraryTarget: 'commonjs2', 16 | devtoolModuleFilenameTemplate: '../[resource-path]' 17 | }, 18 | devtool: 'source-map', 19 | externals: { 20 | vscode: 'commonjs vscode' 21 | }, 22 | resolve: { 23 | extensions: ['.ts', '.js'], 24 | alias: { 25 | 'handlebars': 'handlebars/dist/handlebars.js' 26 | } 27 | }, 28 | module: { 29 | rules: [ 30 | { 31 | test: /\.ts$/, 32 | exclude: /node_modules/, 33 | use: [ 34 | { 35 | loader: 'ts-loader' 36 | } 37 | ] 38 | } 39 | ] 40 | } 41 | }; 42 | module.exports = config; -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Damien Aicheh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /dist/extension.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /**! 2 | 3 | @license 4 | handlebars v4.7.7 5 | 6 | Copyright (C) 2011-2019 by Yehuda Katz 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in 16 | all copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | THE SOFTWARE. 25 | 26 | */ 27 | -------------------------------------------------------------------------------- /src/helpers/file-helper.ts: -------------------------------------------------------------------------------- 1 | import { window, workspace } from 'vscode'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | 5 | export function readFileAsync(path: fs.PathLike) { 6 | return new Promise(function (resolve, reject) { 7 | fs.readFile(path, { encoding: 'utf-8' }, function (error, result) { 8 | if (error) { 9 | reject(error); 10 | } else { 11 | resolve(result); 12 | } 13 | }); 14 | }); 15 | } 16 | 17 | export function createFile(folderPath: string, fileName: string, content: string, successMessage: string, failedMessage: string) { 18 | fs.writeFile(path.join(folderPath, fileName), content, err => { 19 | if (err) { 20 | console.log(err); 21 | return window.showErrorMessage(failedMessage); 22 | } 23 | 24 | window.showInformationMessage(successMessage); 25 | }); 26 | } 27 | 28 | export async function openUntitledTab(content: string, successMessage: string, language: string = 'yaml') { 29 | const document = await workspace.openTextDocument({ 30 | language, 31 | content, 32 | }); 33 | 34 | window.showTextDocument(document); 35 | window.showInformationMessage(successMessage); 36 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azure Pipeline YAML Generator 2 | 3 | Generate Azure DevOps YAML pipelines. 4 | 5 | ## Features 6 | 7 | Generate Azure DevOps YAML pipelines for your projects. 8 | 9 | Currently this extension supports: 10 | - Xamarin.Forms 11 | - Xamarin.iOS 12 | - Xamarin.Android 13 | - iOS 14 | - Android 15 | 16 | Below the list of options generated by the extension for your pipeline depending on the type of your project: 17 | 18 | - Run Unit Test 19 | - Build the project 20 | - Update the package identifier (packageName / BundleID) 21 | - Manage version dynamically 22 | - Add a banner to the launch icon 23 | - Publish the artifacts generated 24 | - Distribute the packages with App Center (.ipa, .apk, .aab) 25 | 26 | ## Contributions 27 | 28 | Feel free to contribute! 29 | 30 | ## Release Notes 31 | 32 | ### 1.0.0 33 | Stable version. 34 | 35 | Supports added for: 36 | - iOS templates 37 | - Android templates 38 | 39 | Fix issues: 40 | - Back button increase the current steps number. 41 | - A folder project was needed to display the file generated. 42 | 43 | ### 0.0.3 44 | Supports added for: 45 | - Xamarin.iOS templates 46 | - Xamarin.Android templates 47 | 48 | Possibility to choose to generate an Android App Bundle or an Apk 49 | 50 | ### 0.0.2 51 | Fix path version fixed for Windows. 52 | 53 | ### 0.0.1 54 | 55 | Demo version: 56 | - Xamarin.Forms templates -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ], 16 | "outFiles": [ 17 | "${workspaceFolder}/dist/**/*.js" 18 | ], 19 | "preLaunchTask": "npm: webpack" 20 | }, 21 | { 22 | "name": "Extension Tests", 23 | "type": "extensionHost", 24 | "request": "launch", 25 | "runtimeExecutable": "${execPath}", 26 | "args": [ 27 | "${workspaceFolder}/testworkspace", 28 | "--disable-extensions", 29 | "--extensionDevelopmentPath=${workspaceFolder}", 30 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 31 | ], 32 | "outFiles": [ 33 | "${workspaceFolder}/out/test/**/*.js" 34 | ], 35 | "preLaunchTask": "npm: test-compile" 36 | }, 37 | { 38 | "name": "Extension Tests - Live Share Integration", 39 | "type": "extensionHost", 40 | "request": "launch", 41 | "runtimeExecutable": "${execPath}", 42 | "args": [ 43 | "${workspaceFolder}/testworkspace", 44 | "--extensionDevelopmentPath=${workspaceFolder}", 45 | "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" 46 | ], 47 | "outFiles": [ 48 | "${workspaceFolder}/out/test/**/*.js" 49 | ], 50 | "preLaunchTask": "npm: test-compile" 51 | } 52 | ] 53 | } -------------------------------------------------------------------------------- /src/generators/yaml-generator.ts: -------------------------------------------------------------------------------- 1 | import { workspace, ExtensionContext } from 'vscode'; 2 | import { InputStep, MultiStepInput } from '../helpers/multi-step-case'; 3 | import * as path from 'path'; 4 | import * as Handlebars from 'handlebars'; 5 | import * as FileHelper from '../helpers/file-helper'; 6 | import * as steps from "./steps"; 7 | 8 | /** 9 | * commentaires 10 | * total step bug when back 11 | * ajout de succeed dans les stage 12 | */ 13 | 14 | export class YamlGenerator { 15 | 16 | template: string = ''; 17 | 18 | vmImage: string = 'vmImage'; 19 | 20 | fileName: string = 'azure-pipelines.yml'; 21 | 22 | projectName: string = 'ProjectName'; 23 | 24 | unitTests: boolean = false; 25 | 26 | useAab: boolean = false; 27 | 28 | automaticVersion: boolean = false; 29 | 30 | updateIdentifier: boolean = false; 31 | 32 | launchIconBadge: boolean = false; 33 | 34 | generateArtifacts: boolean = false; 35 | 36 | distribute: boolean = false; 37 | 38 | currentStep: number = 1; 39 | 40 | steps: InputStep[] = [ 41 | input => steps.chooseFileName(this, input), 42 | input => steps.chooseProjectName(this, input), 43 | ]; 44 | 45 | async generate(context: ExtensionContext) { 46 | await MultiStepInput.runAll(this.steps); 47 | 48 | var templatePath = path.join(context.extensionPath, `/templates/${this.template}`); 49 | 50 | var source = await FileHelper.readFileAsync(templatePath); 51 | 52 | var template = Handlebars.compile(source); 53 | 54 | var result = template(this); 55 | 56 | const folderPath = workspace.rootPath ?? undefined; 57 | 58 | if (folderPath) { 59 | FileHelper.createFile(folderPath, this.fileName, result, 'Generation done', 'Failed to create file'); 60 | } else { 61 | await FileHelper.openUntitledTab(result, 'Generation done'); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import { commands, ExtensionContext } from 'vscode'; 2 | import { XamarinFormsGenerator } from './generators/xamarin-forms-generator'; 3 | import { XamariniOSGenerator } from './generators/xamarin-ios-generator'; 4 | import { XamarinAndroidGenerator } from './generators/xamarin-android-generator'; 5 | import { IOSGenerator } from './generators/ios-generator'; 6 | import { AndroidGenerator } from './generators/android-generator'; 7 | 8 | enum Keys { 9 | XamarinForms = 'xamarin.forms', 10 | XamariniOS = 'xamarin.ios', 11 | XamarinAndroid = 'xamarin.android', 12 | IOS = 'ios', 13 | Android = 'android' 14 | } 15 | 16 | // this method is called when your extension is activated 17 | // your extension is activated the very first time the command is executed 18 | export function activate(context: ExtensionContext) { 19 | commands.registerCommand(`azure-pipeline-yaml-generator.${Keys.XamarinForms}`, async () => { 20 | bootstrap(Keys.XamarinForms, context); 21 | }); 22 | 23 | commands.registerCommand(`azure-pipeline-yaml-generator.${Keys.XamariniOS}`, async () => { 24 | bootstrap(Keys.XamariniOS, context); 25 | }); 26 | 27 | commands.registerCommand(`azure-pipeline-yaml-generator.${Keys.XamarinAndroid}`, async () => { 28 | bootstrap(Keys.XamarinAndroid, context); 29 | }); 30 | 31 | commands.registerCommand(`azure-pipeline-yaml-generator.${Keys.IOS}`, async () => { 32 | bootstrap(Keys.IOS, context); 33 | }); 34 | 35 | commands.registerCommand(`azure-pipeline-yaml-generator.${Keys.Android}`, async () => { 36 | bootstrap(Keys.Android, context); 37 | }); 38 | } 39 | 40 | // this method is called when your extension is deactivated 41 | export function deactivate() { } 42 | 43 | async function bootstrap(key: string, context: ExtensionContext) { 44 | var generator = undefined; 45 | 46 | switch (key) { 47 | case Keys.XamarinForms: 48 | generator = new XamarinFormsGenerator(); 49 | break; 50 | case Keys.XamariniOS: 51 | generator = new XamariniOSGenerator(); 52 | break; 53 | case Keys.XamarinAndroid: 54 | generator = new XamarinAndroidGenerator(); 55 | break; 56 | case Keys.IOS: 57 | generator = new IOSGenerator(); 58 | break; 59 | case Keys.Android: 60 | generator = new AndroidGenerator(); 61 | break; 62 | default: 63 | break; 64 | } 65 | 66 | if (generator) { 67 | generator.generate(context); 68 | } 69 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "azure-pipeline-yaml-generator", 3 | "displayName": "Azure Pipeline YAML Generator", 4 | "description": "", 5 | "version": "1.0.1", 6 | "publisher": "damienaicheh", 7 | "license": "LICENSE.md", 8 | "repository": { 9 | "type": "git", 10 | "url": "https://github.com/damienaicheh/azure-devops-yaml-generator" 11 | }, 12 | "icon": "icon.png", 13 | "engines": { 14 | "vscode": "^1.55.0" 15 | }, 16 | "categories": [ 17 | "Other" 18 | ], 19 | "activationEvents": [ 20 | "*" 21 | ], 22 | "main": "./dist/extension.js", 23 | "contributes": { 24 | "commands": [ 25 | { 26 | "command": "azure-pipeline-yaml-generator.xamarin.forms", 27 | "title": "Xamarin Forms template", 28 | "category": "Azure Pipeline YAML Generator" 29 | }, 30 | { 31 | "command": "azure-pipeline-yaml-generator.xamarin.ios", 32 | "title": "Xamarin iOS template", 33 | "category": "Azure Pipeline YAML Generator" 34 | }, 35 | { 36 | "command": "azure-pipeline-yaml-generator.xamarin.android", 37 | "title": "Xamarin Android template", 38 | "category": "Azure Pipeline YAML Generator" 39 | }, 40 | { 41 | "command": "azure-pipeline-yaml-generator.ios", 42 | "title": "iOS template", 43 | "category": "Azure Pipeline YAML Generator" 44 | }, 45 | { 46 | "command": "azure-pipeline-yaml-generator.android", 47 | "title": "Android template", 48 | "category": "Azure Pipeline YAML Generator" 49 | } 50 | ] 51 | }, 52 | "scripts": { 53 | "compile": "tsc -p ./", 54 | "pretest": "npm run compile && npm run lint", 55 | "vscode:prepublish": "webpack --mode production", 56 | "lint": "eslint ./src --ext .js,.ts", 57 | "lint-fix": "eslint ./src --ext .js,.ts --fix", 58 | "webpack": "webpack --mode none", 59 | "watch": "webpack --mode none --watch", 60 | "pretty": "npx prettier@2 src/. --write", 61 | "just-test": "node ./out/test/runTest.js", 62 | "just-live-share-test": "node out/live-share/test/runTest.js", 63 | "test-compile": "tsc -p ./ && npm run webpack", 64 | "test": "npm run test-compile && npm run just-test", 65 | "test-all": "npm run test && npm run just-live-share-test", 66 | "test:coverage": "npm run just-test -- --coverage" 67 | }, 68 | "devDependencies": { 69 | "@types/glob": "^7.1.3", 70 | "@types/mocha": "^8.2.2", 71 | "@types/node": "^15.0.1", 72 | "@types/vscode": "^1.55.0", 73 | "@typescript-eslint/eslint-plugin": "^4.22.0", 74 | "@typescript-eslint/parser": "^4.22.0", 75 | "eslint": "^7.25.0", 76 | "glob": "^7.1.6", 77 | "mocha": "^8.3.2", 78 | "ts-loader": "^9.1.1", 79 | "typescript": "^4.2.4", 80 | "vscode-test": "^1.5.2", 81 | "webpack": "^5.36.2", 82 | "webpack-cli": "^4.6.0" 83 | }, 84 | "dependencies": { 85 | "fs": "0.0.1-security", 86 | "handlebars": "^4.7.7", 87 | "path": "^0.12.7" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/generators/steps.ts: -------------------------------------------------------------------------------- 1 | import { window, QuickPickItem } from 'vscode'; 2 | import { YamlGenerator } from "./yaml-generator"; 3 | import { MultiStepInput } from '../helpers/multi-step-case'; 4 | 5 | export async function chooseFileName(generator: YamlGenerator, input: MultiStepInput) { 6 | generator.fileName = await inputText(generator, input, 'Give a name to your yaml pipeline file', 'azure-pipelines.yml', 'The file name must finish with .yml extension', validateFileName); 7 | 8 | async function validateFileName(text: string) { 9 | return text.endsWith('.yml') ? undefined : 'Make sure your file extension is \'.yml\''; 10 | } 11 | 12 | window.showInformationMessage(`You choose: ${generator.fileName}`); 13 | } 14 | 15 | export async function chooseProjectName(generator: YamlGenerator, input: MultiStepInput) { 16 | generator.projectName = await inputText(generator, input, 'Give the same project name', 'ProjectName', 'The project name must not have spaces.', validateProjectName); 17 | 18 | async function validateProjectName(text: string) { 19 | return (/^\s*$/.test(text) || /\s/.test(text)) ? 'Make sur you don\'t have any spaces in your project name' : undefined; 20 | } 21 | } 22 | 23 | export async function enableUnitTests(generator: YamlGenerator, input: MultiStepInput) { 24 | generator.unitTests = await booleanPicker(generator, input, 'Do you want to run Unit Tests?'); 25 | } 26 | 27 | export async function androidPackageType(generator: YamlGenerator, input: MultiStepInput) { 28 | generator.useAab = await booleanPicker(generator, input, 'What type of Android package do you want to use?', 'Android App Bundle (Recommanded)', 'Apk'); 29 | } 30 | 31 | export async function manageVersionAutomatically(generator: YamlGenerator, input: MultiStepInput) { 32 | generator.automaticVersion = await booleanPicker(generator, input, 'Manage version automatically? (Git tag required)'); 33 | } 34 | 35 | export async function updateIdentifier(generator: YamlGenerator, input: MultiStepInput) { 36 | generator.updateIdentifier = await booleanPicker(generator, input, 'Manage application identifier?'); 37 | } 38 | 39 | export async function addLaunchIconBadge(generator: YamlGenerator, input: MultiStepInput) { 40 | generator.launchIconBadge = await booleanPicker(generator, input, 'Add launch icon badge?'); 41 | } 42 | 43 | export async function publishArtifacts(generator: YamlGenerator, input: MultiStepInput) { 44 | generator.generateArtifacts = await booleanPicker(generator, input, 'Do you want to publish tha artifacts?'); 45 | } 46 | 47 | export async function enableAppCenterDistribution(generator: YamlGenerator, input: MultiStepInput) { 48 | generator.distribute = await booleanPicker(generator, input, 'Do you want to distribute it using App Center?'); 49 | } 50 | 51 | function shouldResume() { 52 | // Could show a notification with the option to resume. 53 | return new Promise(() => { 54 | 55 | }); 56 | } 57 | 58 | export async function inputText(generator: YamlGenerator, input: MultiStepInput, title: string, defaultValue: string, prompt: string, validate: any) { 59 | 60 | const inputResult = await input.showInputBox({ 61 | title, 62 | value: defaultValue, 63 | step: generator.currentStep, 64 | totalSteps: generator.steps.length, 65 | prompt: prompt, 66 | validate: validate, 67 | shouldResume: shouldResume 68 | }, () => backStep(generator)); 69 | 70 | generator.currentStep++; 71 | 72 | return inputResult; 73 | } 74 | 75 | export async function booleanPicker(generator: YamlGenerator, input: MultiStepInput, title: string, yesOption: string = 'Yes', noOption: string = 'No') { 76 | 77 | const answers: QuickPickItem[] = [yesOption, noOption].map(label => ({ label })); 78 | 79 | const pick = await input.showQuickPick({ 80 | title, 81 | step: generator.currentStep, 82 | totalSteps: generator.steps.length, 83 | items: answers, 84 | shouldResume: shouldResume 85 | }, () => backStep(generator)); 86 | 87 | generator.currentStep++; 88 | 89 | return pick.label === yesOption; 90 | } 91 | 92 | // Back button action detected 93 | function backStep(generator: YamlGenerator): void { 94 | generator.currentStep--; 95 | } -------------------------------------------------------------------------------- /templates/android.stages.yml.tmpl: -------------------------------------------------------------------------------- 1 | # This template was generated with the VS Code extension Azure Pipeline YAML Generator 2 | # A quick tutorial on how to build and sign your Android application using Azure DevOps is available here: 3 | # https://damienaicheh.github.io/azure/devops/2019/06/13/how-to-build-and-sign-your-android-application-using-azure-devops-en 4 | 5 | # Change the vmImage option as you need. 6 | 7 | {{#if updateIdentifier}} 8 | # You need to install this set of extensions to update your identifiers: 9 | # https://marketplace.visualstudio.com/items?itemName=vs-publisher-473885.motz-mobile-buildtasks 10 | {{/if}} 11 | {{#if automaticVersion}} 12 | # You need to install this set of extensions to automatically manage the version of your application: 13 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.mobile-versioning-task 14 | {{else}} 15 | # You need to install a set of extensions to manually manage the version of your application: 16 | # https://marketplace.visualstudio.com/items?itemName=vs-publisher-473885.motz-mobile-buildtasks 17 | {{/if}} 18 | {{#if launchIconBadge}} 19 | # You need to install this extension to add a banner to your launch icon: 20 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.launch-icon-task 21 | {{/if}} 22 | 23 | # Update all the variables just below. 24 | # Assuming you will create a secret group to store all your secure keys and password here are the list of all the variables you need to add: 25 | # - keystore 26 | # - keystore.password 27 | # - keystore.alias 28 | # - keystore.aliasPassword 29 | 30 | {{#if distribute}} 31 | # A quick tutorial to setup Azure DevOps and App Center to deploy your application is available here: 32 | # https://damienaicheh.github.io/azure/devops/appcenter/2019/11/10/setup-azure-devops-and-appcenter-to-deploy-your-application-en.html 33 | {{/if}} 34 | 35 | trigger: none 36 | 37 | pool: 38 | vmImage: {{vmImage}} 39 | 40 | variables: 41 | - group: secret 42 | - name: buildConfiguration 43 | value: 'release' 44 | {{#if updateIdentifier}} 45 | - name: packageName 46 | value: '' 47 | - name: appLabel 48 | value: '' 49 | {{/if}} 50 | {{#unless automaticVersion}} 51 | - name: versionName 52 | value: '1.0' 53 | - name: versionCodeOffset 54 | value: '0' 55 | {{/unless}} 56 | {{#if distribute}} 57 | - name: serverEndpoint # Visual Studio App Center endpoint 58 | value: '' 59 | - name: appSlug 60 | value: '' 61 | - name: distributionGroupId 62 | value: '' 63 | {{/if}} 64 | 65 | stages: 66 | - stage: Build_Android 67 | jobs: 68 | - job: 69 | displayName: 'Build Android' 70 | workspace: 71 | clean: all 72 | steps: 73 | - task: Gradle@2 74 | inputs: 75 | workingDirectory: '$(Build.SourcesDirectory)' 76 | gradleWrapperFile: 'gradlew' 77 | gradleOptions: '-Xmx3072m' 78 | javaHomeOption: 'JDKVersion' 79 | jdkVersionOption: '1.8' 80 | jdkArchitectureOption: 'x64' 81 | publishJUnitResults: false 82 | testResultsFiles: '**/TEST-*.xml' 83 | tasks: 'assemble$(buildConfiguration)' 84 | 85 | {{#if updateIdentifier}} 86 | - task: android-manifest-package-name@1 87 | inputs: 88 | sourcePath: '$(Build.SourcesDirectory)/app/src/main/AndroidManifest.xml' 89 | packageName: '$(packageName)' 90 | appLabel: '$(appLabel)' 91 | printFile: true 92 | {{/if}} 93 | 94 | {{#if automaticVersion}} 95 | - task: ExtractVersionFromTag@1 96 | 97 | - task: UpdateAndroidVersionManifest@1 98 | inputs: 99 | androidManifestPath: '$(Build.SourcesDirectory)/app/src/main/AndroidManifest.xml' 100 | {{else}} 101 | - task: android-manifest-version@1 102 | inputs: 103 | sourcePath: '$(Build.SourcesDirectory)/app/src/main/AndroidManifest.xml' 104 | versionCodeOption: 'buildid' 105 | versionCode: '$(Build.BuildId)' 106 | versionCodeOffset: $(versionCodeOffset) 107 | versionName: '$(versionName).$(Build.BuildId)' 108 | printFile: true 109 | {{/if}} 110 | 111 | {{#if launchIconBadge}} 112 | - task: LaunchIconBadge@1 113 | inputs: 114 | sourceFolder: '$(Build.SourcesDirectory)/app/src/main/res' 115 | contents: '**/ic_launcher.png' 116 | bannerVersionNamePosition: 'bottomRight' 117 | bannerVersionNameText: '$(buildConfiguration)' 118 | {{/if}} 119 | 120 | - task: AndroidSigning@3 121 | inputs: 122 | apkFiles: '**/*.apk' 123 | apksign: true 124 | apksignerKeystoreFile: '$(keystore)' 125 | apksignerKeystorePassword: '$(keystore.password)' 126 | apksignerKeystoreAlias: '$(keystore.alias)' 127 | apksignerKeyPassword: '$(keystore.aliasPassword)' 128 | apksignerArguments: --out $(Build.SourcesDirectory)/app.release.apk 129 | zipalign: true 130 | 131 | {{#if generateArtifacts}} 132 | - task: CopyFiles@2 133 | inputs: 134 | SourceFolder: $(Build.SourcesDirectory) 135 | contents: 'app.release.apk' 136 | targetFolder: 'drop/$(buildConfiguration)_android' 137 | overWrite: true 138 | 139 | - task: PublishBuildArtifacts@1 140 | inputs: 141 | pathtoPublish: 'drop/$(buildConfiguration)_android' 142 | artifactName: '$(buildConfiguration)_android' 143 | publishLocation: 'container' 144 | {{/if}} 145 | 146 | {{#if distribute}} 147 | - stage: Distribute_Android 148 | dependsOn: Build_Android 149 | jobs: 150 | - job: 151 | displayName: 'Distribute Android' 152 | steps: 153 | - task: DownloadBuildArtifacts@0 154 | displayName: 'Download artifacts' 155 | inputs: 156 | buildType: 'current' 157 | downloadType: 'specific' 158 | downloadPath: 'releases_drop' 159 | 160 | - task: AppCenterDistribute@3 161 | displayName: 'Distribute to Visual Studio App Center' 162 | inputs: 163 | serverEndpoint: '$(serverEndpoint)' 164 | appSlug: '$(appSlug)' 165 | appFile: 'releases_drop/$(buildConfiguration)_android/*.apk' 166 | releaseNotesOption: 'input' 167 | releaseNotesInput: 'New Version' 168 | destinationType: 'groups' 169 | distributionGroupId: '$(distributionGroupId)' 170 | {{/if}} -------------------------------------------------------------------------------- /templates/ios.stages.yml.tmpl: -------------------------------------------------------------------------------- 1 | # This template was generated with the VS Code extension Azure Pipeline YAML Generator 2 | # A quick tutorial on how to build and sign your iOS application using Azure DevOps is available here: 3 | # https://damienaicheh.github.io/ios/azure/devops/2019/12/05/build-sign-your-ios-application-using-azure-devops-en.html 4 | 5 | # Change the vmImage option as you need. 6 | 7 | {{#if updateIdentifier}} 8 | # You need to install this set of extensions to update your identifiers: 9 | # https://marketplace.visualstudio.com/items?itemName=vs-publisher-473885.motz-mobile-buildtasks 10 | {{/if}} 11 | {{#if automaticVersion}} 12 | # You need to install this set of extensions to automatically manage the version of your application: 13 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.mobile-versioning-task 14 | {{else}} 15 | # You need to install a set of extensions to manually manage the version of your application: 16 | # https://marketplace.visualstudio.com/items?itemName=vs-publisher-473885.motz-mobile-buildtasks 17 | {{/if}} 18 | {{#if launchIconBadge}} 19 | # You need to install this extension to add a banner to your launch icon: 20 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.launch-icon-task 21 | {{/if}} 22 | 23 | # Update all the variables just below. 24 | # Assuming you will create a secret group to store all your secure keys and password here are the list of all the variables you need to add: 25 | # - p12FileName 26 | # - p12Password 27 | # - provisioningProfile 28 | 29 | {{#if distribute}} 30 | # A quick tutorial to setup Azure DevOps and App Center to deploy your application is available here: 31 | # https://damienaicheh.github.io/azure/devops/appcenter/2019/11/10/setup-azure-devops-and-appcenter-to-deploy-your-application-en.html 32 | {{/if}} 33 | 34 | 35 | trigger: none 36 | 37 | pool: 38 | vmImage: {{vmImage}} 39 | 40 | variables: 41 | - group: secret 42 | - name: Configuration 43 | value: 'Release' 44 | - name: sdk 45 | value: 'iphoneos' 46 | {{#if updateIdentifier}} 47 | - name: bundleIdentifier 48 | value: '' 49 | - name: bundleName 50 | value: '' 51 | - name: bundleDisplayName 52 | value: '' 53 | {{/if}} 54 | {{#unless automaticVersion}} 55 | - name: versionName 56 | value: '1.0' 57 | - name: versionCodeOffset 58 | value: '0' 59 | {{/unless}} 60 | {{#if distribute}} 61 | - name: serverEndpoint # Visual Studio App Center endpoint 62 | value: '' 63 | - name: appSlug 64 | value: '' 65 | - name: distributionGroupId 66 | value: '' 67 | {{/if}} 68 | 69 | stages: 70 | - stage: Build_iOS 71 | jobs: 72 | - job: 73 | displayName: 'Build iOS' 74 | workspace: 75 | clean: all 76 | steps: 77 | - task: InstallAppleCertificate@2 78 | inputs: 79 | certSecureFile: '$(p12FileName)' 80 | certPwd: '$(p12Password)' 81 | keychain: 'temp' 82 | deleteCert: true 83 | 84 | - task: InstallAppleProvisioningProfile@1 85 | inputs: 86 | provisioningProfileLocation: 'secureFiles' 87 | provProfileSecureFile: '$(provisioningProfile)' 88 | removeProfile: true 89 | 90 | - task: CocoaPods@0 91 | inputs: 92 | forceRepoUpdate: false 93 | 94 | {{#if updateIdentifier}} 95 | - task: ios-bundle-identifier@1 96 | inputs: 97 | sourcePath: '$(Build.SourcesDirectory)/{{projectName}}/Info.plist' 98 | bundleIdentifier: '$(bundleIdentifier)' 99 | bundleName: '$(bundleName)' 100 | bundleDisplayName: '$(bundleDisplayName)' 101 | printFile: true 102 | {{/if}} 103 | 104 | {{#if automaticVersion}} 105 | - task: ExtractVersionFromTag@1 106 | 107 | - task: UpdateiOSVersionInfoPlist@1 108 | inputs: 109 | infoPlistPath: '$(Build.SourcesDirectory)/{{projectName}}/Info.plist' 110 | {{else}} 111 | - task: ios-bundle-version@1 112 | inputs: 113 | sourcePath: '$(Build.SourcesDirectory)/{{projectName}}/Info.plist' 114 | versionCodeOption: 'buildid' 115 | versionCode: '$(Build.BuildId)' 116 | versionCodeOffset: '$(versionCodeOffset)' 117 | versionName: '$(versionName).$(Build.BuildId)' 118 | printFile: true 119 | {{/if}} 120 | 121 | {{#if launchIconBadge}} 122 | - task: LaunchIconBadge@1 123 | inputs: 124 | sourceFolder: '$(Build.SourcesDirectory)/{{projectName}}/Assets.xcassets/AppIcon.appiconset' 125 | contents: '**/*.png' 126 | bannerVersionNamePosition: 'bottomRight' 127 | bannerVersionNameText: '$(Configuration)' 128 | {{/if}} 129 | 130 | - task: Xcode@5 131 | inputs: 132 | actions: 'build' 133 | scheme: '{{projectName}}' 134 | sdk: '$(sdk)' 135 | configuration: '$(Configuration)' 136 | xcWorkspacePath: '**/{{projectName}}.xcworkspace' 137 | xcodeVersion: 'default' 138 | packageApp: true 139 | signingOption: 'manual' 140 | signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)' 141 | provisioningProfileUuid: '$(APPLE_PROV_PROFILE_UUID)' 142 | 143 | {{#if generateArtifacts}} 144 | - task: CopyFiles@2 145 | inputs: 146 | contents: '**/*.ipa' 147 | targetFolder: 'drop/$(Configuration)_ios' 148 | overWrite: true 149 | 150 | - task: PublishBuildArtifacts@1 151 | inputs: 152 | pathtoPublish: 'drop/$(Configuration)_ios/output/$(sdk)/$(Configuration)' 153 | artifactName: '$(Configuration)_ios' 154 | {{/if}} 155 | 156 | {{#if distribute}} 157 | - stage: Distribute_iOS 158 | dependsOn: Build_iOS 159 | jobs: 160 | - job: 161 | displayName: 'Distribute iOS' 162 | steps: 163 | 164 | - task: DownloadBuildArtifacts@0 165 | displayName: 'Download artifacts' 166 | inputs: 167 | buildType: 'current' 168 | downloadType: 'specific' 169 | downloadPath: 'releases_drop' 170 | 171 | - task: AppCenterDistribute@3 172 | displayName: 'Distribute to Visual Studio App Center' 173 | inputs: 174 | serverEndpoint: '$(serverEndpoint)' 175 | appSlug: '$(appSlug)' 176 | appFile: 'releases_drop/$(Configuration)_ios/*.ipa' 177 | releaseNotesOption: 'input' 178 | releaseNotesInput: 'New Version' 179 | destinationType: 'groups' 180 | distributionGroupId: '$(distributionGroupId)' 181 | {{/if}} -------------------------------------------------------------------------------- /src/helpers/multi-step-case.ts: -------------------------------------------------------------------------------- 1 | import { window, QuickPickItem, Disposable, QuickInputButton, QuickInput, QuickInputButtons } from 'vscode'; 2 | 3 | // ------------------------------------------------------- 4 | // Helper code that wraps the API for the multi-step case. 5 | // ------------------------------------------------------- 6 | 7 | class InputFlowAction { 8 | private constructor() { } 9 | static back = new InputFlowAction(); 10 | static cancel = new InputFlowAction(); 11 | static resume = new InputFlowAction(); 12 | } 13 | 14 | export type InputStep = (input: MultiStepInput) => Thenable; 15 | 16 | interface QuickPickParameters { 17 | title: string; 18 | items: T[]; 19 | shouldResume: () => Thenable; 20 | placeholder?: string; 21 | step?: number; 22 | totalSteps?: number; 23 | activeItem?: T; 24 | buttons?: QuickInputButton[]; 25 | } 26 | 27 | interface InputBoxParameters { 28 | title: string; 29 | value: string; 30 | prompt: string; 31 | validate: (value: string) => Promise; 32 | shouldResume: () => Thenable; 33 | step?: number; 34 | totalSteps?: number; 35 | buttons?: QuickInputButton[]; 36 | } 37 | 38 | export class MultiStepInput { 39 | 40 | private current?: QuickInput; 41 | private steps: InputStep[] = []; 42 | 43 | static async runAll(steps: InputStep[]) { 44 | const input = new MultiStepInput(); 45 | return input.allStepThrough(steps); 46 | } 47 | 48 | private async allStepThrough(entries: InputStep[]) { 49 | var index = 0; 50 | let step: InputStep | void = entries[0]; 51 | while (index < entries.length) { 52 | this.steps.push(entries[index]); 53 | if (this.current) { 54 | this.current.enabled = false; 55 | this.current.busy = true; 56 | } 57 | try { 58 | step = await entries[index](this); 59 | index++; 60 | } catch (err) { 61 | if (err === InputFlowAction.back) { 62 | this.steps.pop(); 63 | step = this.steps.pop(); 64 | index--; 65 | } else if (err === InputFlowAction.resume) { 66 | step = this.steps.pop(); 67 | index--; 68 | } else if (err === InputFlowAction.cancel) { 69 | step = undefined; 70 | } else { 71 | throw err; 72 | } 73 | } 74 | } 75 | if (this.current) { 76 | this.current.dispose(); 77 | } 78 | } 79 | 80 | async showQuickPick>({ title, step, totalSteps, items, activeItem, placeholder, buttons, shouldResume }: P, back: () => void) { 81 | const disposables: Disposable[] = []; 82 | try { 83 | return await new Promise((resolve, reject) => { 84 | const input = window.createQuickPick(); 85 | input.title = title; 86 | input.step = step; 87 | input.totalSteps = totalSteps; 88 | input.placeholder = placeholder; 89 | input.items = items; 90 | 91 | if (activeItem) { 92 | input.activeItems = [activeItem]; 93 | } 94 | 95 | input.buttons = [ 96 | ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), 97 | ...(buttons || []) 98 | ]; 99 | 100 | disposables.push( 101 | input.onDidTriggerButton(item => { 102 | if (item === QuickInputButtons.Back) { 103 | back(); 104 | reject(InputFlowAction.back); 105 | } else { 106 | resolve(item); 107 | } 108 | }), 109 | input.onDidChangeSelection(items => resolve(items[0])), 110 | input.onDidHide(() => { 111 | (async () => { 112 | reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel); 113 | })() 114 | .catch(reject); 115 | }) 116 | ); 117 | 118 | if (this.current) { 119 | this.current.dispose(); 120 | } 121 | 122 | this.current = input; 123 | this.current.show(); 124 | }); 125 | } finally { 126 | disposables.forEach(d => d.dispose()); 127 | } 128 | } 129 | 130 | async showInputBox

({ title, step, totalSteps, value, prompt, validate, buttons, shouldResume }: P, back: () => void) { 131 | const disposables: Disposable[] = []; 132 | try { 133 | return await new Promise((resolve, reject) => { 134 | const input = window.createInputBox(); 135 | input.title = title; 136 | input.step = step; 137 | input.totalSteps = totalSteps; 138 | input.value = value || ''; 139 | input.prompt = prompt; 140 | input.buttons = [ 141 | ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []), 142 | ...(buttons || []) 143 | ]; 144 | let validating = validate(''); 145 | disposables.push( 146 | input.onDidTriggerButton(item => { 147 | if (item === QuickInputButtons.Back) { 148 | back(); 149 | reject(InputFlowAction.back); 150 | } else { 151 | resolve(item); 152 | } 153 | }), 154 | input.onDidAccept(async () => { 155 | const value = input.value; 156 | input.enabled = false; 157 | input.busy = true; 158 | if (!(await validate(value))) { 159 | resolve(value); 160 | } 161 | input.enabled = true; 162 | input.busy = false; 163 | }), 164 | input.onDidChangeValue(async text => { 165 | const current = validate(text); 166 | validating = current; 167 | const validationMessage = await current; 168 | if (current === validating) { 169 | input.validationMessage = validationMessage; 170 | } 171 | }), 172 | input.onDidHide(() => { 173 | (async () => { 174 | reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel); 175 | })() 176 | .catch(reject); 177 | }) 178 | ); 179 | if (this.current) { 180 | this.current.dispose(); 181 | } 182 | this.current = input; 183 | this.current.show(); 184 | }); 185 | } finally { 186 | disposables.forEach(d => d.dispose()); 187 | } 188 | } 189 | } -------------------------------------------------------------------------------- /templates/xamarin.ios.stages.yml.tmpl: -------------------------------------------------------------------------------- 1 | # This template was generated with the VS Code extension Azure Pipeline YAML Generator 2 | # A quick tutorial on how to build, sign and deploy your Xamarin.iOS application using Azure DevOps and App Center is available here: 3 | # https://damienaicheh.github.io/xamarin/azure/devops/appcenter/2019/11/14/build-sign-and-deploy-your-xamarin-ios-application-using-azure-devops-and-appcenter-en.html 4 | 5 | # Change the vmImage option as you need. 6 | 7 | {{#if updateIdentifier}} 8 | # You need to install this set of extensions to update your identifiers: 9 | # https://marketplace.visualstudio.com/items?itemName=vs-publisher-473885.motz-mobile-buildtasks 10 | {{/if}} 11 | {{#if automaticVersion}} 12 | # You need to install this set of extensions to automatically manage the version of your application: 13 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.mobile-versioning-task 14 | {{else}} 15 | # You need to install a set of extensions to manually manage the version of your application: 16 | # https://marketplace.visualstudio.com/items?itemName=vs-publisher-473885.motz-mobile-buildtasks 17 | {{/if}} 18 | {{#if launchIconBadge}} 19 | # You need to install this extension to add a banner to your launch icon: 20 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.launch-icon-task 21 | {{/if}} 22 | 23 | # Update all the variables just below. 24 | # Assuming you will create a secret group to store all your secure keys and password here are the list of all the variables you need to add: 25 | # - p12FileName 26 | # - p12Password 27 | # - provisioningProfile 28 | 29 | {{#if distribute}} 30 | # A quick tutorial to setup Azure DevOps and App Center to deploy your application is available here: 31 | # https://damienaicheh.github.io/azure/devops/appcenter/2019/11/10/setup-azure-devops-and-appcenter-to-deploy-your-application-en.html 32 | {{/if}} 33 | 34 | trigger: none 35 | 36 | pool: 37 | vmImage: {{vmImage}} 38 | 39 | variables: 40 | - group: secret 41 | - name: xamarinSdkVersion 42 | value: '6_8_0' 43 | - name: solutionPath 44 | value: '**/*.sln' 45 | - name: buildConfiguration 46 | value: 'Release' 47 | {{#if updateIdentifier}} 48 | - name: bundleIdentifier 49 | value: '' 50 | - name: bundleName 51 | value: '' 52 | - name: bundleDisplayName 53 | value: '' 54 | {{/if}} 55 | {{#unless automaticVersion}} 56 | - name: versionName 57 | value: '1.0' 58 | - name: versionCodeOffset 59 | value: '0' 60 | {{/unless}} 61 | {{#if distribute}} 62 | - name: serverEndpoint # Visual Studio App Center endpoint 63 | value: '' 64 | - name: iOSAppSlug 65 | value: '' 66 | - name: iOSDistributionGroupId 67 | value: '' 68 | {{/if}} 69 | 70 | stages: 71 | {{#if unitTests}} 72 | - stage: Run_Unit_Tests 73 | jobs: 74 | - job: 75 | displayName: 'Run Unit Tests' 76 | steps: 77 | - task: NuGetToolInstaller@1 78 | 79 | - task: NuGetCommand@2 80 | inputs: 81 | restoreSolution: '$(solutionPath)' 82 | 83 | - task: UseDotNet@2 84 | displayName: ".NET Core 3.1.x" 85 | inputs: 86 | version: '3.1.x' 87 | packageType: sdk 88 | 89 | - task: DotNetCoreCLI@2 90 | displayName: Build 91 | inputs: 92 | command: build 93 | projects: '$(Build.SourcesDirectory)/{{projectName}}.Tests/*.csproj' 94 | arguments: '--configuration $(buildConfiguration)' 95 | 96 | - task: DotNetCoreCLI@2 97 | inputs: 98 | command: test 99 | projects: '$(Build.SourcesDirectory)/{{projectName}}.Tests/*.csproj' 100 | arguments: '--configuration $(buildConfiguration)' 101 | {{/if}} 102 | 103 | - stage: Build_Xamarin_iOS 104 | {{#if unitTests}} 105 | dependsOn: Run_Unit_Tests 106 | {{/if}} 107 | jobs: 108 | - job: 109 | displayName: 'Build Xamarin.iOS' 110 | workspace: 111 | clean: all 112 | steps: 113 | - task: NuGetToolInstaller@1 114 | 115 | - task: NuGetCommand@2 116 | inputs: 117 | restoreSolution: '$(solutionPath)' 118 | 119 | {{#if updateIdentifier}} 120 | - task: ios-bundle-identifier@1 121 | inputs: 122 | sourcePath: '$(Build.SourcesDirectory)/{{projectName}}/Info.plist' 123 | bundleIdentifier: '$(bundleIdentifier)' 124 | bundleName: '$(bundleName)' 125 | bundleDisplayName: '$(bundleDisplayName)' 126 | printFile: true 127 | {{/if}} 128 | 129 | {{#if automaticVersion}} 130 | - task: ExtractVersionFromTag@1 131 | 132 | - task: UpdateiOSVersionInfoPlist@1 133 | inputs: 134 | infoPlistPath: '$(Build.SourcesDirectory)/{{projectName}}/Info.plist' 135 | {{else}} 136 | - task: ios-bundle-version@1 137 | inputs: 138 | sourcePath: '$(Build.SourcesDirectory)/{{projectName}}/Info.plist' 139 | versionCodeOption: 'buildid' 140 | versionCode: '$(Build.BuildId)' 141 | versionCodeOffset: '$(versionCodeOffset)' 142 | versionName: '$(versionName).$(Build.BuildId)' 143 | printFile: true 144 | {{/if}} 145 | 146 | {{#if launchIconBadge}} 147 | - task: LaunchIconBadge@1 148 | inputs: 149 | sourceFolder: '$(Build.SourcesDirectory)/{{projectName}}/Assets.xcassets/AppIcon.appiconset' 150 | contents: '**/*.png' 151 | bannerVersionNamePosition: 'bottomRight' 152 | bannerVersionNameText: '$(buildConfiguration)' 153 | {{/if}} 154 | 155 | - script: sudo $AGENT_HOMEDIRECTORY/scripts/select-xamarin-sdk.sh $(xamarinSdkVersion) 156 | displayName: 'Select the Xamarin SDK version' 157 | enabled: true 158 | 159 | - task: InstallAppleCertificate@2 160 | inputs: 161 | certSecureFile: '$(p12FileName)' 162 | certPwd: '$(p12Password)' 163 | keychain: 'temp' 164 | deleteCert: true 165 | 166 | - task: InstallAppleProvisioningProfile@1 167 | inputs: 168 | provisioningProfileLocation: 'secureFiles' 169 | provProfileSecureFile: '$(provisioningProfile)' 170 | removeProfile: true 171 | 172 | - task: XamariniOS@2 173 | inputs: 174 | solutionFile: '$(solutionPath)' 175 | configuration: '$(buildConfiguration)' 176 | packageApp: true 177 | buildForSimulator: false 178 | runNugetRestore: false 179 | signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)' 180 | signingProvisioningProfileID: '$(APPLE_PROV_PROFILE_UUID)' 181 | 182 | {{#if generateArtifacts}} 183 | - task: CopyFiles@2 184 | displayName: 'Copy deliverables' 185 | inputs: 186 | SourceFolder: '$(Build.SourcesDirectory)/{{projectName}}/bin/iPhone/$(buildConfiguration)' 187 | Contents: '*.ipa' 188 | TargetFolder: 'drop/$(buildConfiguration)_ios' 189 | 190 | - task: PublishBuildArtifacts@1 191 | displayName: 'Publish release' 192 | inputs: 193 | pathToPublish: 'drop/$(buildConfiguration)_ios' 194 | artifactName: '$(buildConfiguration)_ios' 195 | {{/if}} 196 | 197 | {{#if distribute}} 198 | - stage: Distribute_iOS 199 | dependsOn: Build_Xamarin_iOS 200 | jobs: 201 | - job: 202 | displayName: 'Distribute Xamarin.iOS' 203 | steps: 204 | 205 | - task: DownloadBuildArtifacts@0 206 | displayName: 'Download artifacts' 207 | inputs: 208 | buildType: 'current' 209 | downloadType: 'specific' 210 | downloadPath: 'releases_drop' 211 | 212 | - task: AppCenterDistribute@3 213 | displayName: 'Distribute to Visual Studio App Center' 214 | inputs: 215 | serverEndpoint: '$(serverEndpoint)' 216 | appSlug: '$(iOSAppSlug)' 217 | appFile: 'releases_drop/$(buildConfiguration)_ios/*.ipa' 218 | releaseNotesOption: 'input' 219 | releaseNotesInput: 'New Version' 220 | destinationType: 'groups' 221 | distributionGroupId: '$(iOSDistributionGroupId)' 222 | {{/if}} -------------------------------------------------------------------------------- /templates/xamarin.android.stages.yml.tmpl: -------------------------------------------------------------------------------- 1 | # This template was generated with the VS Code extension Azure Pipeline YAML Generator 2 | # A quick tutorial on how to build, sign and deploy your Xamarin.Android application using Azure DevOps and App Center is available here: 3 | # https://damienaicheh.github.io/xamarin/azure/devops/appcenter/2019/09/10/build-sign-and-deploy-your-xamarin-android-application-using-azure-devops-and-appcenter-en.html 4 | 5 | # Change the vmImage option as you need. 6 | 7 | {{#if updateIdentifier}} 8 | # You need to install this set of extensions to update your identifiers: 9 | # https://marketplace.visualstudio.com/items?itemName=vs-publisher-473885.motz-mobile-buildtasks 10 | {{/if}} 11 | {{#if automaticVersion}} 12 | # You need to install this set of extensions to automatically manage the version of your application: 13 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.mobile-versioning-task 14 | {{else}} 15 | # You need to install a set of extensions to manually manage the version of your application: 16 | # https://marketplace.visualstudio.com/items?itemName=vs-publisher-473885.motz-mobile-buildtasks 17 | {{/if}} 18 | {{#if launchIconBadge}} 19 | # You need to install this extension to add a banner to your launch icon: 20 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.launch-icon-task 21 | {{/if}} 22 | 23 | # Update all the variables just below. 24 | # Assuming you will create a secret group to store all your secure keys and password here are the list of all the variables you need to add: 25 | # - keystore 26 | # - keystore.password 27 | # - keystore.alias 28 | # - keystore.aliasPassword 29 | 30 | {{#if distribute}} 31 | # A quick tutorial to setup Azure DevOps and App Center to deploy your application is available here: 32 | # https://damienaicheh.github.io/azure/devops/appcenter/2019/11/10/setup-azure-devops-and-appcenter-to-deploy-your-application-en.html 33 | {{/if}} 34 | 35 | {{#if useAab}} 36 | # If you want to generate an universal apk for App Center from your Android App Bundle you will need to install this extension: 37 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.bundletool-tasks 38 | # Also to use the InstallBundletool task you need to setup a simple github access token to automatically get the latest version of bundletool. 39 | # Here are the list of the variables you need to add to the secure group: 40 | # - username 41 | # - githubPersonnalAccessToken 42 | 43 | # A tutorial about this is available here: 44 | # https://damienaicheh.github.io/xamarin/azure/devops/2020/05/05/universal-apk-from-aab-with-azure-devops-en.html 45 | {{/if}} 46 | 47 | trigger: none 48 | 49 | pool: 50 | vmImage: {{vmImage}} 51 | 52 | variables: 53 | - group: secret 54 | - name: xamarinSdkVersion 55 | value: '6_8_0' 56 | - name: solutionPath 57 | value: '**/*.sln' 58 | - name: buildConfiguration 59 | value: 'Release' 60 | {{#if updateIdentifier}} 61 | - name: packageName 62 | value: '' 63 | - name: appLabel 64 | value: '' 65 | {{/if}} 66 | {{#unless automaticVersion}} 67 | - name: versionName 68 | value: '1.0' 69 | - name: versionCodeOffset 70 | value: '0' 71 | {{/unless}} 72 | {{#if distribute}} 73 | - name: serverEndpoint # Visual Studio App Center endpoint 74 | value: '' 75 | - name: androidAppSlug 76 | value: '' 77 | - name: androidDistributionGroupId 78 | value: '' 79 | {{/if}} 80 | 81 | stages: 82 | {{#if unitTests}} 83 | - stage: Run_Unit_Tests 84 | jobs: 85 | - job: 86 | displayName: 'Run Unit Tests' 87 | steps: 88 | - task: NuGetToolInstaller@1 89 | 90 | - task: NuGetCommand@2 91 | inputs: 92 | restoreSolution: '$(solutionPath)' 93 | 94 | - task: UseDotNet@2 95 | displayName: ".NET Core 3.1.x" 96 | inputs: 97 | version: '3.1.x' 98 | packageType: sdk 99 | 100 | - task: DotNetCoreCLI@2 101 | displayName: Build 102 | inputs: 103 | command: build 104 | projects: '$(Build.SourcesDirectory)/{{projectName}}.Tests/*.csproj' 105 | arguments: '--configuration $(buildConfiguration)' 106 | 107 | - task: DotNetCoreCLI@2 108 | inputs: 109 | command: test 110 | projects: '$(Build.SourcesDirectory)/{{projectName}}.Tests/*.csproj' 111 | arguments: '--configuration $(buildConfiguration)' 112 | {{/if}} 113 | 114 | - stage: Build_Xamarin_Android 115 | {{#if unitTests}} 116 | dependsOn: Run_Unit_Tests 117 | {{/if}} 118 | jobs: 119 | - job: 120 | displayName: 'Build Xamarin.Android' 121 | workspace: 122 | clean: all 123 | steps: 124 | - task: NuGetToolInstaller@1 125 | 126 | - task: NuGetCommand@2 127 | inputs: 128 | restoreSolution: '$(solutionPath)' 129 | 130 | {{#if updateIdentifier}} 131 | - task: android-manifest-package-name@1 132 | inputs: 133 | sourcePath: '$(Build.SourcesDirectory)/{{projectName}}/Properties/AndroidManifest.xml' 134 | packageName: '$(packageName)' 135 | appLabel: '$(appLabel)' 136 | printFile: true 137 | {{/if}} 138 | 139 | {{#if automaticVersion}} 140 | - task: ExtractVersionFromTag@1 141 | 142 | - task: UpdateAndroidVersionManifest@1 143 | inputs: 144 | androidManifestPath: '$(Build.SourcesDirectory)/{{projectName}}/Properties/AndroidManifest.xml' 145 | {{else}} 146 | - task: android-manifest-version@1 147 | inputs: 148 | sourcePath: '$(Build.SourcesDirectory)/{{projectName}}/Properties/AndroidManifest.xml' 149 | versionCodeOption: 'buildid' 150 | versionCode: '$(Build.BuildId)' 151 | versionCodeOffset: $(versionCodeOffset) 152 | versionName: '$(versionName).$(Build.BuildId)' 153 | printFile: true 154 | {{/if}} 155 | 156 | {{#if launchIconBadge}} 157 | - task: LaunchIconBadge@1 158 | inputs: 159 | sourceFolder: '$(Build.SourcesDirectory)/{{projectName}}/Resources' 160 | contents: '**/icon.png' 161 | bannerVersionNamePosition: 'bottomRight' 162 | bannerVersionNameText: '$(buildConfiguration)' 163 | {{/if}} 164 | 165 | - script: sudo $AGENT_HOMEDIRECTORY/scripts/select-xamarin-sdk.sh $(xamarinSdkVersion) 166 | displayName: 'Select the Xamarin SDK version' 167 | enabled: true 168 | 169 | - task: DownloadSecureFile@1 170 | name: keyStore 171 | displayName: "Download keystore from secure files" 172 | inputs: 173 | secureFile: '$(keystore)' 174 | 175 | - task: Bash@3 176 | displayName: "Build Android package" 177 | inputs: 178 | targetType: "inline" 179 | script: | 180 | msbuild -restore $(Build.SourcesDirectory)/{{projectName}}/*.csproj -t:SignAndroidPackage -p:AndroidPackageFormat={{#if useAab}}aab{{else}}apk{{/if}} -p:Configuration=$(buildConfiguration) -p:AndroidKeyStore=True -p:AndroidSigningKeyStore=$(keyStore.secureFilePath) -p:AndroidSigningStorePass=$(keystore.password) -p:AndroidSigningKeyAlias=$(keystore.alias) -p:AndroidSigningKeyPass=$(keystore.aliasPassword) 181 | 182 | {{#if generateArtifacts}} 183 | - task: CopyFiles@2 184 | displayName: 'Copy deliverables' 185 | inputs: 186 | SourceFolder: '$(Build.SourcesDirectory)/{{projectName}}/bin/$(buildConfiguration)' 187 | Contents: {{#if useAab}}'*Signed.aab'{{else}}'*Signed.apk'{{/if}} 188 | TargetFolder: 'drop/$(buildConfiguration)_android' 189 | 190 | - task: PublishBuildArtifacts@1 191 | displayName: 'Publish release' 192 | inputs: 193 | pathToPublish: 'drop/$(buildConfiguration)_android' 194 | artifactName: '$(buildConfiguration)_android' 195 | {{/if}} 196 | 197 | {{#if distribute}} 198 | - stage: Distribute_Android 199 | dependsOn: Build_Xamarin_Android 200 | jobs: 201 | - job: 202 | displayName: 'Distribute Xamarin.Android' 203 | steps: 204 | - task: DownloadBuildArtifacts@0 205 | displayName: 'Download artifacts' 206 | inputs: 207 | buildType: 'current' 208 | downloadType: 'specific' 209 | downloadPath: 'releases_drop' 210 | 211 | {{#if useAab}} 212 | - task: DownloadSecureFile@1 213 | name: keyStore 214 | displayName: "Download keystore from secure files" 215 | inputs: 216 | secureFile: '$(keystore)' 217 | 218 | - task: InstallBundletool@1 219 | inputs: 220 | username: '$(username)' 221 | personalAccessToken: '$(githubPersonnalAccessToken)' 222 | 223 | - task: AabConvertToUniversalApk@1 224 | inputs: 225 | aabFilePath: 'releases_drop/$(buildConfiguration)_android/*.aab' 226 | keystoreFilePath: '$(keyStore.secureFilePath)' 227 | keystorePassword: '$(keystore.password)' 228 | keystoreAlias: '$(keystore.alias)' 229 | keystoreAliasPassword: '$(keystore.aliasPassword)' 230 | outputFolder: 'releases_drop/$(buildConfiguration)_android/tmp' 231 | 232 | - task: AppCenterDistribute@3 233 | displayName: 'Distribute to Visual Studio App Center' 234 | inputs: 235 | serverEndpoint: '$(serverEndpoint)' 236 | appSlug: '$(androidAppSlug)' 237 | appFile: 'releases_drop/$(buildConfiguration)_android/tmp/*.apk' 238 | releaseNotesOption: 'input' 239 | releaseNotesInput: 'New Version' 240 | destinationType: 'groups' 241 | distributionGroupId: '$(androidDistributionGroupId)' 242 | 243 | {{else}} 244 | 245 | - task: AppCenterDistribute@3 246 | displayName: 'Distribute to Visual Studio App Center' 247 | inputs: 248 | serverEndpoint: '$(serverEndpoint)' 249 | appSlug: '$(androidAppSlug)' 250 | appFile: 'releases_drop/$(buildConfiguration)_android/*.apk' 251 | releaseNotesOption: 'input' 252 | releaseNotesInput: 'New Version' 253 | destinationType: 'groups' 254 | distributionGroupId: '$(androidDistributionGroupId)' 255 | {{/if}} 256 | {{/if}} -------------------------------------------------------------------------------- /templates/xamarin.forms.stages.yml.tmpl: -------------------------------------------------------------------------------- 1 | # This template was generated with the VS Code extension Azure Pipeline YAML Generator 2 | # A complete series on how to manage your Xamarin Forms application using Azure DevOps is available here: 3 | # https://damienaicheh.github.io/xamarin/azure/devops/2020/02/27/run-unit-tests-using-xamarin-and-azure-devops-en.html 4 | 5 | # Change the vmImage option as you need. 6 | 7 | {{#if updateIdentifier}} 8 | # You need to install this set of extensions to update your identifiers: 9 | # https://marketplace.visualstudio.com/items?itemName=vs-publisher-473885.motz-mobile-buildtasks 10 | {{/if}} 11 | {{#if automaticVersion}} 12 | # You need to install this set of extensions to automatically manage the version of your application: 13 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.mobile-versioning-task 14 | {{else}} 15 | # You need to install a set of extensions to manually manage the version of your application: 16 | # https://marketplace.visualstudio.com/items?itemName=vs-publisher-473885.motz-mobile-buildtasks 17 | {{/if}} 18 | {{#if launchIconBadge}} 19 | # You need to install this extension to add a banner to your launch icon: 20 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.launch-icon-task 21 | {{/if}} 22 | 23 | # Update all the variables just below. 24 | # Assuming you will create a secret group to store all your secure keys and password here are the list of all the variables you need to add: 25 | # - keystore 26 | # - keystore.password 27 | # - keystore.alias 28 | # - keystore.aliasPassword 29 | # - p12FileName 30 | # - p12Password 31 | # - provisioningProfile 32 | 33 | # A quick tutorial to setup Azure DevOps and App Center to deploy your application is available here: 34 | # https://damienaicheh.github.io/azure/devops/appcenter/2019/11/10/setup-azure-devops-and-appcenter-to-deploy-your-application-en.html 35 | 36 | {{#if distribute}} 37 | # A quick tutorial to setup Azure DevOps and App Center to deploy your application is available here: 38 | # https://damienaicheh.github.io/azure/devops/appcenter/2019/11/10/setup-azure-devops-and-appcenter-to-deploy-your-application-en.html 39 | {{/if}} 40 | 41 | {{#if useAab}} 42 | # If you want to generate an universal apk for App Center from your Android App Bundle you will need to install this extension: 43 | # https://marketplace.visualstudio.com/items?itemName=DamienAicheh.bundletool-tasks 44 | # Also to use the InstallBundletool task you need to setup a simple github access token to automatically get the latest version of bundletool. 45 | # Here are the list of the variables you need to add to the secure group: 46 | # - username 47 | # - githubPersonnalAccessToken 48 | 49 | # A tutorial about this is available here: 50 | # https://damienaicheh.github.io/xamarin/azure/devops/2020/05/05/universal-apk-from-aab-with-azure-devops-en.html 51 | {{/if}} 52 | 53 | trigger: none 54 | 55 | pool: 56 | vmImage: {{vmImage}} 57 | 58 | variables: 59 | - group: secret 60 | - name: xamarinSdkVersion 61 | value: '6_8_0' 62 | - name: solutionPath 63 | value: '**/*.sln' 64 | - name: buildConfiguration 65 | value: 'Release' 66 | {{#if updateIdentifier}} 67 | - name: packageName 68 | value: '' 69 | - name: appLabel 70 | value: '' 71 | - name: bundleIdentifier 72 | value: '' 73 | - name: bundleName 74 | value: '' 75 | - name: bundleDisplayName 76 | value: '' 77 | {{/if}} 78 | {{#unless automaticVersion}} 79 | - name: versionName 80 | value: '1.0' 81 | - name: versionCodeOffset 82 | value: '0' 83 | {{/unless}} 84 | {{#if distribute}} 85 | - name: serverEndpoint # Visual Studio App Center endpoint 86 | value: '' 87 | - name: iOSAppSlug 88 | value: '' 89 | - name: androidAppSlug 90 | value: '' 91 | - name: iOSDistributionGroupId 92 | value: '' 93 | - name: androidDistributionGroupId 94 | value: '' 95 | {{/if}} 96 | 97 | stages: 98 | {{#if unitTests}} 99 | - stage: Run_Unit_Tests 100 | jobs: 101 | - job: 102 | displayName: 'Run Unit Tests' 103 | steps: 104 | - task: NuGetToolInstaller@1 105 | 106 | - task: NuGetCommand@2 107 | inputs: 108 | restoreSolution: '$(solutionPath)' 109 | 110 | - task: UseDotNet@2 111 | displayName: ".NET Core 3.1.x" 112 | inputs: 113 | version: '3.1.x' 114 | packageType: sdk 115 | 116 | - task: DotNetCoreCLI@2 117 | displayName: Build 118 | inputs: 119 | command: build 120 | projects: '$(Build.SourcesDirectory)/{{projectName}}.Tests/*.csproj' 121 | arguments: '--configuration $(buildConfiguration)' 122 | 123 | - task: DotNetCoreCLI@2 124 | inputs: 125 | command: test 126 | projects: '$(Build.SourcesDirectory)/{{projectName}}.Tests/*.csproj' 127 | arguments: '--configuration $(buildConfiguration)' 128 | {{/if}} 129 | 130 | - stage: Build_Xamarin_Android 131 | {{#if unitTests}} 132 | dependsOn: Run_Unit_Tests 133 | {{/if}} 134 | jobs: 135 | - job: 136 | displayName: 'Build Xamarin.Android' 137 | workspace: 138 | clean: all 139 | steps: 140 | - task: NuGetToolInstaller@1 141 | 142 | - task: NuGetCommand@2 143 | inputs: 144 | restoreSolution: '$(solutionPath)' 145 | 146 | {{#if updateIdentifier}} 147 | - task: android-manifest-package-name@1 148 | inputs: 149 | sourcePath: '$(Build.SourcesDirectory)/{{projectName}}.Android/Properties/AndroidManifest.xml' 150 | packageName: '$(packageName)' 151 | appLabel: '$(appLabel)' 152 | printFile: true 153 | {{/if}} 154 | 155 | {{#if automaticVersion}} 156 | - task: ExtractVersionFromTag@1 157 | 158 | - task: UpdateAndroidVersionManifest@1 159 | inputs: 160 | androidManifestPath: '$(Build.SourcesDirectory)/{{projectName}}.Android/Properties/AndroidManifest.xml' 161 | {{else}} 162 | - task: android-manifest-version@1 163 | inputs: 164 | sourcePath: '$(Build.SourcesDirectory)/{{projectName}}.Android/Properties/AndroidManifest.xml' 165 | versionCodeOption: 'buildid' 166 | versionCode: '$(Build.BuildId)' 167 | versionCodeOffset: $(versionCodeOffset) 168 | versionName: '$(versionName).$(Build.BuildId)' 169 | printFile: true 170 | {{/if}} 171 | 172 | {{#if launchIconBadge}} 173 | - task: LaunchIconBadge@1 174 | inputs: 175 | sourceFolder: '$(Build.SourcesDirectory)/{{projectName}}.Android/Resources' 176 | contents: '**/icon.png' 177 | bannerVersionNamePosition: 'bottomRight' 178 | bannerVersionNameText: '$(buildConfiguration)' 179 | {{/if}} 180 | 181 | - script: sudo $AGENT_HOMEDIRECTORY/scripts/select-xamarin-sdk.sh $(xamarinSdkVersion) 182 | displayName: 'Select the Xamarin SDK version' 183 | enabled: true 184 | 185 | - task: DownloadSecureFile@1 186 | name: keyStore 187 | displayName: "Download keystore from secure files" 188 | inputs: 189 | secureFile: '$(keystore)' 190 | 191 | - task: Bash@3 192 | displayName: "Build Android package" 193 | inputs: 194 | targetType: "inline" 195 | script: | 196 | msbuild -restore $(Build.SourcesDirectory)/{{projectName}}.Android/*.csproj -t:SignAndroidPackage -p:AndroidPackageFormat={{#if useAab}}aab{{else}}apk{{/if}} -p:Configuration=$(buildConfiguration) -p:AndroidKeyStore=True -p:AndroidSigningKeyStore=$(keyStore.secureFilePath) -p:AndroidSigningStorePass=$(keystore.password) -p:AndroidSigningKeyAlias=$(keystore.alias) -p:AndroidSigningKeyPass=$(keystore.aliasPassword) 197 | 198 | {{#if generateArtifacts}} 199 | - task: CopyFiles@2 200 | displayName: 'Copy deliverables' 201 | inputs: 202 | SourceFolder: '$(Build.SourcesDirectory)/{{projectName}}.Android/bin/$(buildConfiguration)' 203 | Contents: {{#if useAab}}'*Signed.aab'{{else}}'*Signed.apk'{{/if}} 204 | TargetFolder: 'drop/$(buildConfiguration)_android' 205 | 206 | - task: PublishBuildArtifacts@1 207 | displayName: 'Publish release' 208 | inputs: 209 | pathToPublish: 'drop/$(buildConfiguration)_android' 210 | artifactName: '$(buildConfiguration)_android' 211 | {{/if}} 212 | 213 | - stage: Build_Xamarin_iOS 214 | {{#if unitTests}} 215 | dependsOn: Run_Unit_Tests 216 | {{/if}} 217 | jobs: 218 | - job: 219 | displayName: 'Build Xamarin.iOS' 220 | workspace: 221 | clean: all 222 | steps: 223 | - task: NuGetToolInstaller@1 224 | 225 | - task: NuGetCommand@2 226 | inputs: 227 | restoreSolution: '$(solutionPath)' 228 | 229 | {{#if updateIdentifier}} 230 | - task: ios-bundle-identifier@1 231 | inputs: 232 | sourcePath: '$(Build.SourcesDirectory)/{{projectName}}.iOS/Info.plist' 233 | bundleIdentifier: '$(bundleIdentifier)' 234 | bundleName: '$(bundleName)' 235 | bundleDisplayName: '$(bundleDisplayName)' 236 | printFile: true 237 | {{/if}} 238 | 239 | {{#if automaticVersion}} 240 | - task: ExtractVersionFromTag@1 241 | 242 | - task: UpdateiOSVersionInfoPlist@1 243 | inputs: 244 | infoPlistPath: '$(Build.SourcesDirectory)/{{projectName}}.iOS/Info.plist' 245 | {{else}} 246 | - task: ios-bundle-version@1 247 | inputs: 248 | sourcePath: '$(Build.SourcesDirectory)/{{projectName}}.iOS/Info.plist' 249 | versionCodeOption: 'buildid' 250 | versionCode: '$(Build.BuildId)' 251 | versionCodeOffset: '$(versionCodeOffset)' 252 | versionName: '$(versionName).$(Build.BuildId)' 253 | printFile: true 254 | {{/if}} 255 | 256 | {{#if launchIconBadge}} 257 | - task: LaunchIconBadge@1 258 | inputs: 259 | sourceFolder: '$(Build.SourcesDirectory)/{{projectName}}.iOS/Assets.xcassets/AppIcon.appiconset' 260 | contents: '**/*.png' 261 | bannerVersionNamePosition: 'bottomRight' 262 | bannerVersionNameText: '$(buildConfiguration)' 263 | {{/if}} 264 | 265 | - script: sudo $AGENT_HOMEDIRECTORY/scripts/select-xamarin-sdk.sh $(xamarinSdkVersion) 266 | displayName: 'Select the Xamarin SDK version' 267 | enabled: true 268 | 269 | - task: InstallAppleCertificate@2 270 | inputs: 271 | certSecureFile: '$(p12FileName)' 272 | certPwd: '$(p12Password)' 273 | keychain: 'temp' 274 | deleteCert: true 275 | 276 | - task: InstallAppleProvisioningProfile@1 277 | inputs: 278 | provisioningProfileLocation: 'secureFiles' 279 | provProfileSecureFile: '$(provisioningProfile)' 280 | removeProfile: true 281 | 282 | - task: XamariniOS@2 283 | inputs: 284 | solutionFile: '$(solutionPath)' 285 | configuration: '$(buildConfiguration)' 286 | packageApp: true 287 | buildForSimulator: false 288 | runNugetRestore: false 289 | signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)' 290 | signingProvisioningProfileID: '$(APPLE_PROV_PROFILE_UUID)' 291 | 292 | {{#if generateArtifacts}} 293 | - task: CopyFiles@2 294 | displayName: 'Copy deliverables' 295 | inputs: 296 | SourceFolder: '$(Build.SourcesDirectory)/{{projectName}}.iOS/bin/iPhone/$(buildConfiguration)' 297 | Contents: '*.ipa' 298 | TargetFolder: 'drop/$(buildConfiguration)_ios' 299 | 300 | - task: PublishBuildArtifacts@1 301 | displayName: 'Publish release' 302 | inputs: 303 | pathToPublish: 'drop/$(buildConfiguration)_ios' 304 | artifactName: '$(buildConfiguration)_ios' 305 | {{/if}} 306 | 307 | {{#if distribute}} 308 | - stage: Distribute_Android 309 | dependsOn: Build_Xamarin_Android 310 | jobs: 311 | - job: 312 | displayName: 'Distribute Xamarin.Android' 313 | steps: 314 | - task: DownloadBuildArtifacts@0 315 | displayName: 'Download artifacts' 316 | inputs: 317 | buildType: 'current' 318 | downloadType: 'specific' 319 | downloadPath: 'releases_drop' 320 | 321 | {{#if useAab}} 322 | - task: DownloadSecureFile@1 323 | name: keyStore 324 | displayName: "Download keystore from secure files" 325 | inputs: 326 | secureFile: '$(keystore)' 327 | 328 | - task: InstallBundletool@1 329 | inputs: 330 | username: '$(username)' 331 | personalAccessToken: '$(githubPersonnalAccessToken)' 332 | 333 | - task: AabConvertToUniversalApk@1 334 | inputs: 335 | aabFilePath: 'releases_drop/$(buildConfiguration)_android/*.aab' 336 | keystoreFilePath: '$(keyStore.secureFilePath)' 337 | keystorePassword: '$(keystore.password)' 338 | keystoreAlias: '$(keystore.alias)' 339 | keystoreAliasPassword: '$(keystore.aliasPassword)' 340 | outputFolder: 'releases_drop/$(buildConfiguration)_android/tmp' 341 | 342 | - task: AppCenterDistribute@3 343 | displayName: 'Distribute to Visual Studio App Center' 344 | inputs: 345 | serverEndpoint: '$(serverEndpoint)' 346 | appSlug: '$(androidAppSlug)' 347 | appFile: 'releases_drop/$(buildConfiguration)_android/tmp/*.apk' 348 | releaseNotesOption: 'input' 349 | releaseNotesInput: 'New Version' 350 | destinationType: 'groups' 351 | distributionGroupId: '$(androidDistributionGroupId)' 352 | 353 | {{else}} 354 | - task: AppCenterDistribute@3 355 | displayName: 'Distribute to Visual Studio App Center' 356 | inputs: 357 | serverEndpoint: '$(serverEndpoint)' 358 | appSlug: '$(androidAppSlug)' 359 | appFile: 'releases_drop/$(buildConfiguration)_android/*.apk' 360 | releaseNotesOption: 'input' 361 | releaseNotesInput: 'New Version' 362 | destinationType: 'groups' 363 | distributionGroupId: '$(androidDistributionGroupId)' 364 | {{/if}} 365 | 366 | 367 | 368 | - stage: Distribute_iOS 369 | dependsOn: Build_Xamarin_iOS 370 | jobs: 371 | - job: 372 | displayName: 'Distribute Xamarin.iOS' 373 | steps: 374 | 375 | - task: DownloadBuildArtifacts@0 376 | displayName: 'Download artifacts' 377 | inputs: 378 | buildType: 'current' 379 | downloadType: 'specific' 380 | downloadPath: 'releases_drop' 381 | 382 | - task: AppCenterDistribute@3 383 | displayName: 'Distribute to Visual Studio App Center' 384 | inputs: 385 | serverEndpoint: '$(serverEndpoint)' 386 | appSlug: '$(iOSAppSlug)' 387 | appFile: 'releases_drop/$(buildConfiguration)_ios/*.ipa' 388 | releaseNotesOption: 'input' 389 | releaseNotesInput: 'New Version' 390 | destinationType: 'groups' 391 | distributionGroupId: '$(iOSDistributionGroupId)' 392 | {{/if}} -------------------------------------------------------------------------------- /dist/extension.js: -------------------------------------------------------------------------------- 1 | /*! For license information please see extension.js.LICENSE.txt */ 2 | (()=>{var t={675:function(t){var e;e=function(){return function(t){var e={};function r(i){if(e[i])return e[i].exports;var n=e[i]={exports:{},id:i,loaded:!1};return t[i].call(n.exports,n,n.exports,r),n.loaded=!0,n.exports}return r.m=t,r.c=e,r.p="",r(0)}([function(t,e,r){"use strict";var i=r(1).default;e.__esModule=!0;var n=i(r(2)),s=i(r(45)),o=r(46),a=r(51),c=i(r(52)),u=i(r(49)),l=i(r(44)),h=n.default.create;function p(){var t=h();return t.compile=function(e,r){return a.compile(e,r,t)},t.precompile=function(e,r){return a.precompile(e,r,t)},t.AST=s.default,t.Compiler=a.Compiler,t.JavaScriptCompiler=c.default,t.Parser=o.parser,t.parse=o.parse,t.parseWithoutProcessing=o.parseWithoutProcessing,t}var d=p();d.create=p,l.default(d),d.Visitor=u.default,d.default=d,e.default=d,t.exports=e.default},function(t,e){"use strict";e.default=function(t){return t&&t.__esModule?t:{default:t}},e.__esModule=!0},function(t,e,r){"use strict";var i=r(3).default,n=r(1).default;e.__esModule=!0;var s=i(r(4)),o=n(r(37)),a=n(r(6)),c=i(r(5)),u=i(r(38)),l=n(r(44));function h(){var t=new s.HandlebarsEnvironment;return c.extend(t,s),t.SafeString=o.default,t.Exception=a.default,t.Utils=c,t.escapeExpression=c.escapeExpression,t.VM=u,t.template=function(e){return u.template(e,t)},t}var p=h();p.create=h,l.default(p),p.default=p,e.default=p,t.exports=e.default},function(t,e){"use strict";e.default=function(t){if(t&&t.__esModule)return t;var e={};if(null!=t)for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&(e[r]=t[r]);return e.default=t,e},e.__esModule=!0},function(t,e,r){"use strict";var i=r(1).default;e.__esModule=!0,e.HandlebarsEnvironment=h;var n=r(5),s=i(r(6)),o=r(10),a=r(30),c=i(r(32)),u=r(33);e.VERSION="4.7.7",e.COMPILER_REVISION=8,e.LAST_COMPATIBLE_COMPILER_REVISION=7,e.REVISION_CHANGES={1:"<= 1.0.rc.2",2:"== 1.0.0-rc.3",3:"== 1.0.0-rc.4",4:"== 1.x.x",5:"== 2.0.0-alpha.x",6:">= 2.0.0-beta.1",7:">= 4.0.0 <4.3.0",8:">= 4.3.0"};var l="[object Object]";function h(t,e,r){this.helpers=t||{},this.partials=e||{},this.decorators=r||{},o.registerDefaultHelpers(this),a.registerDefaultDecorators(this)}h.prototype={constructor:h,logger:c.default,log:c.default.log,registerHelper:function(t,e){if(n.toString.call(t)===l){if(e)throw new s.default("Arg not supported with multiple helpers");n.extend(this.helpers,t)}else this.helpers[t]=e},unregisterHelper:function(t){delete this.helpers[t]},registerPartial:function(t,e){if(n.toString.call(t)===l)n.extend(this.partials,t);else{if(void 0===e)throw new s.default('Attempting to register a partial called "'+t+'" as undefined');this.partials[t]=e}},unregisterPartial:function(t){delete this.partials[t]},registerDecorator:function(t,e){if(n.toString.call(t)===l){if(e)throw new s.default("Arg not supported with multiple decorators");n.extend(this.decorators,t)}else this.decorators[t]=e},unregisterDecorator:function(t){delete this.decorators[t]},resetLoggedPropertyAccesses:function(){u.resetLoggedProperties()}};var p=c.default.log;e.log=p,e.createFrame=n.createFrame,e.logger=c.default},function(t,e){"use strict";e.__esModule=!0,e.extend=o,e.indexOf=function(t,e){for(var r=0,i=t.length;r":">",'"':""","'":"'","`":"`","=":"="},i=/[&<>"'`=]/g,n=/[&<>"'`=]/;function s(t){return r[t]}function o(t){for(var e=1;e0?(r.ids&&(r.ids=[r.name]),t.helpers.each(e,r)):n(this);if(r.data&&r.ids){var o=i.createFrame(r.data);o.contextPath=i.appendContextPath(r.data.contextPath,r.name),r={data:o}}return s(e,r)}))},t.exports=e.default},function(t,e,r){(function(i){"use strict";var n=r(13).default,s=r(1).default;e.__esModule=!0;var o=r(5),a=s(r(6));e.default=function(t){t.registerHelper("each",(function(t,e){if(!e)throw new a.default("Must pass iterator to #each");var r,s=e.fn,c=e.inverse,u=0,l="",h=void 0,p=void 0;function d(e,r,i){h&&(h.key=e,h.index=r,h.first=0===r,h.last=!!i,p&&(h.contextPath=p+e)),l+=s(t[e],{data:h,blockParams:o.blockParams([t[e],e],[p+e,null])})}if(e.data&&e.ids&&(p=o.appendContextPath(e.data.contextPath,e.ids[0])+"."),o.isFunction(t)&&(t=t.call(this)),e.data&&(h=o.createFrame(e.data)),t&&"object"==typeof t)if(o.isArray(t))for(var f=t.length;u=0?e:parseInt(t,10)}return t},log:function(t){if(t=n.lookupLevel(t),"undefined"!=typeof console&&n.lookupLevel(n.level)<=t){var e=n.methodMap[t];console[e]||(e="log");for(var r=arguments.length,i=Array(r>1?r-1:0),s=1;s=u.LAST_COMPATIBLE_COMPILER_REVISION&&e<=u.COMPILER_REVISION)){if(e2&&k.push("'"+this.terminals_[v]+"'");_=this.lexer.showPosition?"Parse error on line "+(o+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+k.join(", ")+", got '"+(this.terminals_[h]||h)+"'":"Parse error on line "+(o+1)+": Unexpected "+(1==h?"end of input":"'"+(this.terminals_[h]||h)+"'"),this.parseError(_,{text:this.lexer.match,token:this.terminals_[h]||h,line:this.lexer.yylineno,loc:u,expected:k})}}if(f[0]instanceof Array&&f.length>1)throw new Error("Parse Error: multiple actions possible at state: "+d+", token: "+h);switch(f[0]){case 1:e.push(h),r.push(this.lexer.yytext),i.push(this.lexer.yylloc),e.push(f[1]),h=null,p?(h=p,p=null):(a=this.lexer.yyleng,s=this.lexer.yytext,o=this.lexer.yylineno,u=this.lexer.yylloc,c>0&&c--);break;case 2:if(g=this.productions_[f[1]][1],S.$=r[r.length-g],S._$={first_line:i[i.length-(g||1)].first_line,last_line:i[i.length-1].last_line,first_column:i[i.length-(g||1)].first_column,last_column:i[i.length-1].last_column},l&&(S._$.range=[i[i.length-(g||1)].range[0],i[i.length-1].range[1]]),void 0!==(m=this.performAction.call(S,s,a,o,this.yy,f[1],r,i)))return m;g&&(e=e.slice(0,-1*g*2),r=r.slice(0,-1*g),i=i.slice(0,-1*g)),e.push(this.productions_[f[1]][0]),r.push(S.$),i.push(S._$),y=n[e[e.length-2]][e[e.length-1]],e.push(y);break;case 3:return!0}}return!0}},e={EOF:1,parseError:function(t,e){if(!this.yy.parser)throw new Error(t);this.yy.parser.parseError(t,e)},setInput:function(t){return this._input=t,this._more=this._less=this.done=!1,this.yylineno=this.yyleng=0,this.yytext=this.matched=this.match="",this.conditionStack=["INITIAL"],this.yylloc={first_line:1,first_column:0,last_line:1,last_column:0},this.options.ranges&&(this.yylloc.range=[0,0]),this.offset=0,this},input:function(){var t=this._input[0];return this.yytext+=t,this.yyleng++,this.offset++,this.match+=t,this.matched+=t,t.match(/(?:\r\n?|\n).*/g)?(this.yylineno++,this.yylloc.last_line++):this.yylloc.last_column++,this.options.ranges&&this.yylloc.range[1]++,this._input=this._input.slice(1),t},unput:function(t){var e=t.length,r=t.split(/(?:\r\n?|\n)/g);this._input=t+this._input,this.yytext=this.yytext.substr(0,this.yytext.length-e-1),this.offset-=e;var i=this.match.split(/(?:\r\n?|\n)/g);this.match=this.match.substr(0,this.match.length-1),this.matched=this.matched.substr(0,this.matched.length-1),r.length-1&&(this.yylineno-=r.length-1);var n=this.yylloc.range;return this.yylloc={first_line:this.yylloc.first_line,last_line:this.yylineno+1,first_column:this.yylloc.first_column,last_column:r?(r.length===i.length?this.yylloc.first_column:0)+i[i.length-r.length].length-r[0].length:this.yylloc.first_column-e},this.options.ranges&&(this.yylloc.range=[n[0],n[0]+this.yyleng-e]),this},more:function(){return this._more=!0,this},less:function(t){this.unput(this.match.slice(t))},pastInput:function(){var t=this.matched.substr(0,this.matched.length-this.match.length);return(t.length>20?"...":"")+t.substr(-20).replace(/\n/g,"")},upcomingInput:function(){var t=this.match;return t.length<20&&(t+=this._input.substr(0,20-t.length)),(t.substr(0,20)+(t.length>20?"...":"")).replace(/\n/g,"")},showPosition:function(){var t=this.pastInput(),e=new Array(t.length+1).join("-");return t+this.upcomingInput()+"\n"+e+"^"},next:function(){if(this.done)return this.EOF;var t,e,r,i,n;this._input||(this.done=!0),this._more||(this.yytext="",this.match="");for(var s=this._currentRules(),o=0;oe[0].length)||(e=r,i=o,this.options.flex));o++);return e?((n=e[0].match(/(?:\r\n?|\n).*/g))&&(this.yylineno+=n.length),this.yylloc={first_line:this.yylloc.last_line,last_line:this.yylineno+1,first_column:this.yylloc.last_column,last_column:n?n[n.length-1].length-n[n.length-1].match(/\r?\n?/)[0].length:this.yylloc.last_column+e[0].length},this.yytext+=e[0],this.match+=e[0],this.matches=e,this.yyleng=this.yytext.length,this.options.ranges&&(this.yylloc.range=[this.offset,this.offset+=this.yyleng]),this._more=!1,this._input=this._input.slice(e[0].length),this.matched+=e[0],t=this.performAction.call(this,this.yy,this,s[i],this.conditionStack[this.conditionStack.length-1]),this.done&&this._input&&(this.done=!1),t||void 0):""===this._input?this.EOF:this.parseError("Lexical error on line "+(this.yylineno+1)+". Unrecognized text.\n"+this.showPosition(),{text:"",token:null,line:this.yylineno})},lex:function(){var t=this.next();return void 0!==t?t:this.lex()},begin:function(t){this.conditionStack.push(t)},popState:function(){return this.conditionStack.pop()},_currentRules:function(){return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules},topState:function(){return this.conditionStack[this.conditionStack.length-2]},pushState:function(t){this.begin(t)},options:{},performAction:function(t,e,r,i){function n(t,r){return e.yytext=e.yytext.substring(t,e.yyleng-r+t)}switch(r){case 0:if("\\\\"===e.yytext.slice(-2)?(n(0,1),this.begin("mu")):"\\"===e.yytext.slice(-1)?(n(0,1),this.begin("emu")):this.begin("mu"),e.yytext)return 15;break;case 1:return 15;case 2:return this.popState(),15;case 3:return this.begin("raw"),15;case 4:return this.popState(),"raw"===this.conditionStack[this.conditionStack.length-1]?15:(n(5,9),"END_RAW_BLOCK");case 5:return 15;case 6:return this.popState(),14;case 7:return 65;case 8:return 68;case 9:return 19;case 10:return this.popState(),this.begin("raw"),23;case 11:return 55;case 12:return 60;case 13:return 29;case 14:return 47;case 15:case 16:return this.popState(),44;case 17:return 34;case 18:return 39;case 19:return 51;case 20:return 48;case 21:this.unput(e.yytext),this.popState(),this.begin("com");break;case 22:return this.popState(),14;case 23:return 48;case 24:return 73;case 25:case 26:return 72;case 27:return 87;case 28:break;case 29:return this.popState(),54;case 30:return this.popState(),33;case 31:return e.yytext=n(1,2).replace(/\\"/g,'"'),80;case 32:return e.yytext=n(1,2).replace(/\\'/g,"'"),80;case 33:return 85;case 34:case 35:return 82;case 36:return 83;case 37:return 84;case 38:return 81;case 39:return 75;case 40:return 77;case 41:return 72;case 42:return e.yytext=e.yytext.replace(/\\([\\\]])/g,"$1"),72;case 43:return"INVALID";case 44:return 5}},rules:[/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:\{\{\{\{(?=[^\/]))/,/^(?:\{\{\{\{\/[^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=[=}\s\/.])\}\}\}\})/,/^(?:[^\x00]+?(?=(\{\{\{\{)))/,/^(?:[\s\S]*?--(~)?\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{\{\{)/,/^(?:\}\}\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#>)/,/^(?:\{\{(~)?#\*?)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^\s*(~)?\}\})/,/^(?:\{\{(~)?\s*else\s*(~)?\}\})/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{(~)?!--)/,/^(?:\{\{(~)?![\s\S]*?\}\})/,/^(?:\{\{(~)?\*?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)|])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:undefined(?=([~}\s)])))/,/^(?:null(?=([~}\s)])))/,/^(?:-?[0-9]+(?:\.[0-9]+)?(?=([~}\s)])))/,/^(?:as\s+\|)/,/^(?:\|)/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)|]))))/,/^(?:\[(\\\]|[^\]])*\])/,/^(?:.)/,/^(?:$)/],conditions:{mu:{rules:[7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44],inclusive:!1},emu:{rules:[2],inclusive:!1},com:{rules:[6],inclusive:!1},raw:{rules:[3,4,5],inclusive:!1},INITIAL:{rules:[0,1,44],inclusive:!0}}};function r(){this.yy={}}return t.lexer=e,r.prototype=t,t.Parser=r,new r}();e.default=r,t.exports=e.default},function(t,e,r){"use strict";var i=r(1).default;e.__esModule=!0;var n=i(r(49));function s(){var t=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];this.options=t}function o(t,e,r){void 0===e&&(e=t.length);var i=t[e-1],n=t[e-2];return i?"ContentStatement"===i.type?(n||!r?/\r?\n\s*?$/:/(^|\r?\n)\s*?$/).test(i.original):void 0:r}function a(t,e,r){void 0===e&&(e=-1);var i=t[e+1],n=t[e+2];return i?"ContentStatement"===i.type?(n||!r?/^\s*?\r?\n/:/^\s*?(\r?\n|$)/).test(i.original):void 0:r}function c(t,e,r){var i=t[null==e?0:e+1];if(i&&"ContentStatement"===i.type&&(r||!i.rightStripped)){var n=i.value;i.value=i.value.replace(r?/^\s+/:/^[ \t]*\r?\n?/,""),i.rightStripped=i.value!==n}}function u(t,e,r){var i=t[null==e?t.length-1:e-1];if(i&&"ContentStatement"===i.type&&(r||!i.leftStripped)){var n=i.value;return i.value=i.value.replace(r?/\s+$/:/[ \t]+$/,""),i.leftStripped=i.value!==n,i.leftStripped}}s.prototype=new n.default,s.prototype.Program=function(t){var e=!this.options.ignoreStandalone,r=!this.isRootSeen;this.isRootSeen=!0;for(var i=t.body,n=0,s=i.length;n0)throw new n.default("Invalid path: "+i,{loc:r});".."===u&&o++}}return{type:"PathExpression",data:t,depth:o,parts:s,original:i,loc:r}},e.prepareMustache=function(t,e,r,i,n,s){var o=i.charAt(3)||i.charAt(2),a="{"!==o&&"&"!==o;return{type:/\*/.test(i)?"Decorator":"MustacheStatement",path:t,params:e,hash:r,escaped:a,strip:n,loc:this.locInfo(s)}},e.prepareRawBlock=function(t,e,r,i){s(t,r);var n={type:"Program",body:e,strip:{},loc:i=this.locInfo(i)};return{type:"BlockStatement",path:t.path,params:t.params,hash:t.hash,program:n,openStrip:{},inverseStrip:{},closeStrip:{},loc:i}},e.prepareBlock=function(t,e,r,i,o,a){i&&i.path&&s(t,i);var c=/\*/.test(t.open);e.blockParams=t.blockParams;var u=void 0,l=void 0;if(r){if(c)throw new n.default("Unexpected inverse block on decorator",r);r.chain&&(r.program.body[0].closeStrip=i.strip),l=r.strip,u=r.program}return o&&(o=u,u=e,e=o),{type:c?"DecoratorBlock":"BlockStatement",path:t.path,params:t.params,hash:t.hash,program:e,inverse:u,openStrip:t.strip,inverseStrip:l,closeStrip:i&&i.strip,loc:this.locInfo(a)}},e.prepareProgram=function(t,e){if(!e&&t.length){var r=t[0].loc,i=t[t.length-1].loc;r&&i&&(e={source:r.source,start:{line:r.start.line,column:r.start.column},end:{line:i.end.line,column:i.end.column}})}return{type:"Program",body:t,strip:{},loc:e}},e.preparePartialBlock=function(t,e,r,i){return s(t,r),{type:"PartialBlockStatement",name:t.path,params:t.params,hash:t.hash,program:e,openStrip:t.strip,closeStrip:r&&r.strip,loc:this.locInfo(i)}};var n=i(r(6));function s(t,e){if(e=e.path?e.path.original:e,t.path.original!==e){var r={loc:t.path.loc};throw new n.default(t.path.original+" doesn't match "+e,r)}}},function(t,e,r){"use strict";var i=r(34).default,n=r(1).default;e.__esModule=!0,e.Compiler=u,e.precompile=function(t,e,r){if(null==t||"string"!=typeof t&&"Program"!==t.type)throw new s.default("You must pass a string or Handlebars AST to Handlebars.precompile. You passed "+t);"data"in(e=e||{})||(e.data=!0),e.compat&&(e.useDepths=!0);var i=r.parse(t,e),n=(new r.Compiler).compile(i,e);return(new r.JavaScriptCompiler).compile(n,e)},e.compile=function(t,e,r){if(void 0===e&&(e={}),null==t||"string"!=typeof t&&"Program"!==t.type)throw new s.default("You must pass a string or Handlebars AST to Handlebars.compile. You passed "+t);"data"in(e=o.extend({},e))||(e.data=!0),e.compat&&(e.useDepths=!0);var i=void 0;function n(){var i=r.parse(t,e),n=(new r.Compiler).compile(i,e),s=(new r.JavaScriptCompiler).compile(n,e,void 0,!0);return r.template(s)}function a(t,e){return i||(i=n()),i.call(this,t,e)}return a._setup=function(t){return i||(i=n()),i._setup(t)},a._child=function(t,e,r,s){return i||(i=n()),i._child(t,e,r,s)},a};var s=n(r(6)),o=r(5),a=n(r(45)),c=[].slice;function u(){}function l(t,e){if(t===e)return!0;if(o.isArray(t)&&o.isArray(e)&&t.length===e.length){for(var r=0;r1)throw new s.default("Unsupported number of partial arguments: "+r.length,t);r.length||(this.options.explicitPartialContext?this.opcode("pushLiteral","undefined"):r.push({type:"PathExpression",parts:[],depth:0}));var i=t.name.original,n="SubExpression"===t.name.type;n&&this.accept(t.name),this.setupFullMustacheParams(t,e,void 0,!0);var o=t.indent||"";this.options.preventIndent&&o&&(this.opcode("appendContent",o),o=""),this.opcode("invokePartial",n,i,o),this.opcode("append")},PartialBlockStatement:function(t){this.PartialStatement(t)},MustacheStatement:function(t){this.SubExpression(t),t.escaped&&!this.options.noEscape?this.opcode("appendEscaped"):this.opcode("append")},Decorator:function(t){this.DecoratorBlock(t)},ContentStatement:function(t){t.value&&this.opcode("appendContent",t.value)},CommentStatement:function(){},SubExpression:function(t){h(t);var e=this.classifySexpr(t);"simple"===e?this.simpleSexpr(t):"helper"===e?this.helperSexpr(t):this.ambiguousSexpr(t)},ambiguousSexpr:function(t,e,r){var i=t.path,n=i.parts[0],s=null!=e||null!=r;this.opcode("getContext",i.depth),this.opcode("pushProgram",e),this.opcode("pushProgram",r),i.strict=!0,this.accept(i),this.opcode("invokeAmbiguous",n,s)},simpleSexpr:function(t){var e=t.path;e.strict=!0,this.accept(e),this.opcode("resolvePossibleLambda")},helperSexpr:function(t,e,r){var i=this.setupFullMustacheParams(t,e,r),n=t.path,o=n.parts[0];if(this.options.knownHelpers[o])this.opcode("invokeKnownHelper",i.length,o);else{if(this.options.knownHelpersOnly)throw new s.default("You specified knownHelpersOnly, but used the unknown helper "+o,t);n.strict=!0,n.falsy=!0,this.accept(n),this.opcode("invokeHelper",i.length,n.original,a.default.helpers.simpleId(n))}},PathExpression:function(t){this.addDepth(t.depth),this.opcode("getContext",t.depth);var e=t.parts[0],r=a.default.helpers.scopedId(t),i=!t.depth&&!r&&this.blockParamIndex(e);i?this.opcode("lookupBlockParam",i,t.parts):e?t.data?(this.options.data=!0,this.opcode("lookupData",t.depth,t.parts,t.strict)):this.opcode("lookupOnContext",t.parts,t.falsy,t.strict,r):this.opcode("pushContext")},StringLiteral:function(t){this.opcode("pushString",t.value)},NumberLiteral:function(t){this.opcode("pushLiteral",t.value)},BooleanLiteral:function(t){this.opcode("pushLiteral",t.value)},UndefinedLiteral:function(){this.opcode("pushLiteral","undefined")},NullLiteral:function(){this.opcode("pushLiteral","null")},Hash:function(t){var e=t.pairs,r=0,i=e.length;for(this.opcode("pushHash");r=0)return[e,n]}}}},function(t,e,r){"use strict";var i=r(13).default,n=r(1).default;e.__esModule=!0;var s=r(4),o=n(r(6)),a=r(5),c=n(r(53));function u(t){this.value=t}function l(){}l.prototype={nameLookup:function(t,e){return this.internalNameLookup(t,e)},depthedLookup:function(t){return[this.aliasable("container.lookup"),"(depths, ",JSON.stringify(t),")"]},compilerInfo:function(){var t=s.COMPILER_REVISION;return[t,s.REVISION_CHANGES[t]]},appendToBuffer:function(t,e,r){return a.isArray(t)||(t=[t]),t=this.source.wrap(t,e),this.environment.isSimple?["return ",t,";"]:r?["buffer += ",t,";"]:(t.appendToBuffer=!0,t)},initializeBuffer:function(){return this.quotedString("")},internalNameLookup:function(t,e){return this.lookupPropertyFunctionIsUsed=!0,["lookupProperty(",t,",",JSON.stringify(e),")"]},lookupPropertyFunctionIsUsed:!1,compile:function(t,e,r,i){this.environment=t,this.options=e,this.stringParams=this.options.stringParams,this.trackIds=this.options.trackIds,this.precompile=!i,this.name=this.environment.name,this.isChild=!!r,this.context=r||{decorators:[],programs:[],environments:[]},this.preamble(),this.stackSlot=0,this.stackVars=[],this.aliases={},this.registers={list:[]},this.hashes=[],this.compileStack=[],this.inlineStack=[],this.blockParams=[],this.compileChildren(t,e),this.useDepths=this.useDepths||t.useDepths||t.useDecorators||this.options.compat,this.useBlockParams=this.useBlockParams||t.useBlockParams;var n=t.opcodes,s=void 0,a=void 0,c=void 0,u=void 0;for(c=0,u=n.length;c0&&(r+=", "+n.join(", "));var s=0;i(this.aliases).forEach((function(t){var i=e.aliases[t];i.children&&i.referenceCount>1&&(r+=", alias"+ ++s+"="+t,i.children[0]="alias"+s)})),this.lookupPropertyFunctionIsUsed&&(r+=", "+this.lookupPropertyFunctionVarDeclaration());var o=["container","depth0","helpers","partials","data"];(this.useBlockParams||this.useDepths)&&o.push("blockParams"),this.useDepths&&o.push("depths");var a=this.mergeSource(r);return t?(o.push(a),Function.apply(this,o)):this.source.wrap(["function(",o.join(","),") {\n ",a,"}"])},mergeSource:function(t){var e=this.environment.isSimple,r=!this.forceBuffer,i=void 0,n=void 0,s=void 0,o=void 0;return this.source.each((function(t){t.appendToBuffer?(s?t.prepend(" + "):s=t,o=t):(s&&(n?s.prepend("buffer += "):i=!0,o.add(";"),s=o=void 0),n=!0,e||(r=!1))})),r?s?(s.prepend("return "),o.add(";")):n||this.source.push('return "";'):(t+=", buffer = "+(i?"":this.initializeBuffer()),s?(s.prepend("return buffer + "),o.add(";")):this.source.push("return buffer;")),t&&this.source.prepend("var "+t.substring(2)+(i?"":";\n")),this.source.merge()},lookupPropertyFunctionVarDeclaration:function(){return"\n lookupProperty = container.lookupProperty || function(parent, propertyName) {\n if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {\n return parent[propertyName];\n }\n return undefined\n }\n ".trim()},blockValue:function(t){var e=this.aliasable("container.hooks.blockHelperMissing"),r=[this.contextName(0)];this.setupHelperArgs(t,0,r);var i=this.popStack();r.splice(1,0,i),this.push(this.source.functionCall(e,"call",r))},ambiguousBlockValue:function(){var t=this.aliasable("container.hooks.blockHelperMissing"),e=[this.contextName(0)];this.setupHelperArgs("",0,e,!0),this.flushInline();var r=this.topStack();e.splice(1,0,r),this.pushSource(["if (!",this.lastHelper,") { ",r," = ",this.source.functionCall(t,"call",e),"}"])},appendContent:function(t){this.pendingContent?t=this.pendingContent+t:this.pendingLocation=this.source.currentLocation,this.pendingContent=t},append:function(){if(this.isInline())this.replaceStack((function(t){return[" != null ? ",t,' : ""']})),this.pushSource(this.appendToBuffer(this.popStack()));else{var t=this.popStack();this.pushSource(["if (",t," != null) { ",this.appendToBuffer(t,void 0,!0)," }"]),this.environment.isSimple&&this.pushSource(["else { ",this.appendToBuffer("''",void 0,!0)," }"])}},appendEscaped:function(){this.pushSource(this.appendToBuffer([this.aliasable("container.escapeExpression"),"(",this.popStack(),")"]))},getContext:function(t){this.lastContext=t},pushContext:function(){this.pushStackLiteral(this.contextName(this.lastContext))},lookupOnContext:function(t,e,r,i){var n=0;i||!this.options.compat||this.lastContext?this.pushContext():this.push(this.depthedLookup(t[n++])),this.resolvePath("context",t,n,e,r)},lookupBlockParam:function(t,e){this.useBlockParams=!0,this.push(["blockParams[",t[0],"][",t[1],"]"]),this.resolvePath("context",e,1)},lookupData:function(t,e,r){t?this.pushStackLiteral("container.data(data, "+t+")"):this.pushStackLiteral("data"),this.resolvePath("data",e,0,!0,r)},resolvePath:function(t,e,r,i,n){var s=this;if(this.options.strict||this.options.assumeObjects)this.push(function(t,e,r,i){var n=e.popStack(),s=0,o=r.length;for(t&&o--;sthis.stackVars.length&&this.stackVars.push("stack"+this.stackSlot),this.topStackName()},topStackName:function(){return"stack"+this.stackSlot},flushInline:function(){var t=this.inlineStack;this.inlineStack=[];for(var e=0,r=t.length;ei(this,void 0,void 0,(function*(){h(l.XamarinForms,t)})))),n.commands.registerCommand(`azure-pipeline-yaml-generator.${l.XamariniOS}`,(()=>i(this,void 0,void 0,(function*(){h(l.XamariniOS,t)})))),n.commands.registerCommand(`azure-pipeline-yaml-generator.${l.XamarinAndroid}`,(()=>i(this,void 0,void 0,(function*(){h(l.XamarinAndroid,t)})))),n.commands.registerCommand(`azure-pipeline-yaml-generator.${l.IOS}`,(()=>i(this,void 0,void 0,(function*(){h(l.IOS,t)})))),n.commands.registerCommand(`azure-pipeline-yaml-generator.${l.Android}`,(()=>i(this,void 0,void 0,(function*(){h(l.Android,t)}))))},e.deactivate=function(){}},80:(t,e,r)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.AndroidGenerator=void 0;const i=r(919),n=r(725);class s extends i.YamlGenerator{constructor(){super(),this.template="android.stages.yml.tmpl",this.vmImage="macOS-latest",this.steps=this.steps.concat([t=>n.manageVersionAutomatically(this,t),t=>n.updateIdentifier(this,t),t=>n.addLaunchIconBadge(this,t),t=>n.publishArtifacts(this,t),t=>n.enableAppCenterDistribution(this,t)])}}e.AndroidGenerator=s},538:(t,e,r)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.IOSGenerator=void 0;const i=r(919),n=r(725);class s extends i.YamlGenerator{constructor(){super(),this.template="ios.stages.yml.tmpl",this.vmImage="macOS-latest",this.steps=this.steps.concat([t=>n.manageVersionAutomatically(this,t),t=>n.updateIdentifier(this,t),t=>n.addLaunchIconBadge(this,t),t=>n.publishArtifacts(this,t),t=>n.enableAppCenterDistribution(this,t)])}}e.IOSGenerator=s},725:function(t,e,r){"use strict";var i=this&&this.__awaiter||function(t,e,r,i){return new(r||(r=Promise))((function(n,s){function o(t){try{c(i.next(t))}catch(t){s(t)}}function a(t){try{c(i.throw(t))}catch(t){s(t)}}function c(t){var e;t.done?n(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(o,a)}c((i=i.apply(t,e||[])).next())}))};Object.defineProperty(e,"__esModule",{value:!0}),e.booleanPicker=e.inputText=e.enableAppCenterDistribution=e.publishArtifacts=e.addLaunchIconBadge=e.updateIdentifier=e.manageVersionAutomatically=e.androidPackageType=e.enableUnitTests=e.chooseProjectName=e.chooseFileName=void 0;const n=r(549);function s(){return new Promise((()=>{}))}function o(t,e,r,n,o,a){return i(this,void 0,void 0,(function*(){const i=yield e.showInputBox({title:r,value:n,step:t.currentStep,totalSteps:t.steps.length,prompt:o,validate:a,shouldResume:s},(()=>c(t)));return t.currentStep++,i}))}function a(t,e,r,n="Yes",o="No"){return i(this,void 0,void 0,(function*(){const i=[n,o].map((t=>({label:t}))),a=yield e.showQuickPick({title:r,step:t.currentStep,totalSteps:t.steps.length,items:i,shouldResume:s},(()=>c(t)));return t.currentStep++,a.label===n}))}function c(t){t.currentStep--}e.chooseFileName=function(t,e){return i(this,void 0,void 0,(function*(){t.fileName=yield o(t,e,"Give a name to your yaml pipeline file","azure-pipelines.yml","The file name must finish with .yml extension",(function(t){return i(this,void 0,void 0,(function*(){return t.endsWith(".yml")?void 0:"Make sure your file extension is '.yml'"}))})),n.window.showInformationMessage(`You choose: ${t.fileName}`)}))},e.chooseProjectName=function(t,e){return i(this,void 0,void 0,(function*(){t.projectName=yield o(t,e,"Give the same project name","ProjectName","The project name must not have spaces.",(function(t){return i(this,void 0,void 0,(function*(){return/^\s*$/.test(t)||/\s/.test(t)?"Make sur you don't have any spaces in your project name":void 0}))}))}))},e.enableUnitTests=function(t,e){return i(this,void 0,void 0,(function*(){t.unitTests=yield a(t,e,"Do you want to run Unit Tests?")}))},e.androidPackageType=function(t,e){return i(this,void 0,void 0,(function*(){t.useAab=yield a(t,e,"What type of Android package do you want to use?","Android App Bundle (Recommanded)","Apk")}))},e.manageVersionAutomatically=function(t,e){return i(this,void 0,void 0,(function*(){t.automaticVersion=yield a(t,e,"Manage version automatically? (Git tag required)")}))},e.updateIdentifier=function(t,e){return i(this,void 0,void 0,(function*(){t.updateIdentifier=yield a(t,e,"Manage application identifier?")}))},e.addLaunchIconBadge=function(t,e){return i(this,void 0,void 0,(function*(){t.launchIconBadge=yield a(t,e,"Add launch icon badge?")}))},e.publishArtifacts=function(t,e){return i(this,void 0,void 0,(function*(){t.generateArtifacts=yield a(t,e,"Do you want to publish tha artifacts?")}))},e.enableAppCenterDistribution=function(t,e){return i(this,void 0,void 0,(function*(){t.distribute=yield a(t,e,"Do you want to distribute it using App Center?")}))},e.inputText=o,e.booleanPicker=a},959:(t,e,r)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.XamarinAndroidGenerator=void 0;const i=r(919),n=r(725);class s extends i.YamlGenerator{constructor(){super(),this.template="xamarin.android.stages.yml.tmpl",this.vmImage="macOS-latest",this.steps=this.steps.concat([t=>n.enableUnitTests(this,t),t=>n.androidPackageType(this,t),t=>n.manageVersionAutomatically(this,t),t=>n.updateIdentifier(this,t),t=>n.addLaunchIconBadge(this,t),t=>n.publishArtifacts(this,t),t=>n.enableAppCenterDistribution(this,t)])}}e.XamarinAndroidGenerator=s},757:(t,e,r)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.XamarinFormsGenerator=void 0;const i=r(919),n=r(725);class s extends i.YamlGenerator{constructor(){super(),this.template="xamarin.forms.stages.yml.tmpl",this.vmImage="macOS-latest",this.steps=this.steps.concat([t=>n.enableUnitTests(this,t),t=>n.androidPackageType(this,t),t=>n.manageVersionAutomatically(this,t),t=>n.updateIdentifier(this,t),t=>n.addLaunchIconBadge(this,t),t=>n.publishArtifacts(this,t),t=>n.enableAppCenterDistribution(this,t)])}}e.XamarinFormsGenerator=s},21:(t,e,r)=>{"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.XamariniOSGenerator=void 0;const i=r(919),n=r(725);class s extends i.YamlGenerator{constructor(){super(),this.template="xamarin.ios.stages.yml.tmpl",this.vmImage="macOS-latest",this.steps=this.steps.concat([t=>n.enableUnitTests(this,t),t=>n.manageVersionAutomatically(this,t),t=>n.updateIdentifier(this,t),t=>n.addLaunchIconBadge(this,t),t=>n.publishArtifacts(this,t),t=>n.enableAppCenterDistribution(this,t)])}}e.XamariniOSGenerator=s},919:function(t,e,r){"use strict";var i=this&&this.__awaiter||function(t,e,r,i){return new(r||(r=Promise))((function(n,s){function o(t){try{c(i.next(t))}catch(t){s(t)}}function a(t){try{c(i.throw(t))}catch(t){s(t)}}function c(t){var e;t.done?n(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(o,a)}c((i=i.apply(t,e||[])).next())}))};Object.defineProperty(e,"__esModule",{value:!0}),e.YamlGenerator=void 0;const n=r(549),s=r(290),o=r(622),a=r(675),c=r(921),u=r(725);e.YamlGenerator=class{constructor(){this.template="",this.vmImage="vmImage",this.fileName="azure-pipelines.yml",this.projectName="ProjectName",this.unitTests=!1,this.useAab=!1,this.automaticVersion=!1,this.updateIdentifier=!1,this.launchIconBadge=!1,this.generateArtifacts=!1,this.distribute=!1,this.currentStep=1,this.steps=[t=>u.chooseFileName(this,t),t=>u.chooseProjectName(this,t)]}generate(t){var e;return i(this,void 0,void 0,(function*(){yield s.MultiStepInput.runAll(this.steps);var r=o.join(t.extensionPath,`/templates/${this.template}`),i=yield c.readFileAsync(r),u=a.compile(i)(this);const l=null!==(e=n.workspace.rootPath)&&void 0!==e?e:void 0;l?c.createFile(l,this.fileName,u,"Generation done","Failed to create file"):yield c.openUntitledTab(u,"Generation done")}))}}},921:function(t,e,r){"use strict";var i=this&&this.__awaiter||function(t,e,r,i){return new(r||(r=Promise))((function(n,s){function o(t){try{c(i.next(t))}catch(t){s(t)}}function a(t){try{c(i.throw(t))}catch(t){s(t)}}function c(t){var e;t.done?n(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(o,a)}c((i=i.apply(t,e||[])).next())}))};Object.defineProperty(e,"__esModule",{value:!0}),e.openUntitledTab=e.createFile=e.readFileAsync=void 0;const n=r(549),s=r(747),o=r(622);e.readFileAsync=function(t){return new Promise((function(e,r){s.readFile(t,{encoding:"utf-8"},(function(t,i){t?r(t):e(i)}))}))},e.createFile=function(t,e,r,i,a){s.writeFile(o.join(t,e),r,(t=>{if(t)return console.log(t),n.window.showErrorMessage(a);n.window.showInformationMessage(i)}))},e.openUntitledTab=function(t,e,r="yaml"){return i(this,void 0,void 0,(function*(){const i=yield n.workspace.openTextDocument({language:r,content:t});n.window.showTextDocument(i),n.window.showInformationMessage(e)}))}},290:function(t,e,r){"use strict";var i=this&&this.__awaiter||function(t,e,r,i){return new(r||(r=Promise))((function(n,s){function o(t){try{c(i.next(t))}catch(t){s(t)}}function a(t){try{c(i.throw(t))}catch(t){s(t)}}function c(t){var e;t.done?n(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e)}))).then(o,a)}c((i=i.apply(t,e||[])).next())}))};Object.defineProperty(e,"__esModule",{value:!0}),e.MultiStepInput=void 0;const n=r(549);class s{constructor(){}}s.back=new s,s.cancel=new s,s.resume=new s;class o{constructor(){this.steps=[]}static runAll(t){return i(this,void 0,void 0,(function*(){return(new o).allStepThrough(t)}))}allStepThrough(t){return i(this,void 0,void 0,(function*(){var e=0;let r=t[0];for(;e{const m=n.window.createQuickPick();m.title=t,m.step=e,m.totalSteps=r,m.placeholder=c,m.items=o,a&&(m.activeItems=[a]),m.buttons=[...this.steps.length>1?[n.QuickInputButtons.Back]:[],...u||[]],p.push(m.onDidTriggerButton((t=>{t===n.QuickInputButtons.Back?(h(),f(s.back)):d(t)})),m.onDidChangeSelection((t=>d(t[0]))),m.onDidHide((()=>{(()=>i(this,void 0,void 0,(function*(){f(l&&(yield l())?s.resume:s.cancel)})))().catch(f)}))),this.current&&this.current.dispose(),this.current=m,this.current.show()}))}finally{p.forEach((t=>t.dispose()))}}))}showInputBox({title:t,step:e,totalSteps:r,value:o,prompt:a,validate:c,buttons:u,shouldResume:l},h){return i(this,void 0,void 0,(function*(){const p=[];try{return yield new Promise(((d,f)=>{const m=n.window.createInputBox();m.title=t,m.step=e,m.totalSteps=r,m.value=o||"",m.prompt=a,m.buttons=[...this.steps.length>1?[n.QuickInputButtons.Back]:[],...u||[]];let v=c("");p.push(m.onDidTriggerButton((t=>{t===n.QuickInputButtons.Back?(h(),f(s.back)):d(t)})),m.onDidAccept((()=>i(this,void 0,void 0,(function*(){const t=m.value;m.enabled=!1,m.busy=!0,(yield c(t))||d(t),m.enabled=!0,m.busy=!1})))),m.onDidChangeValue((t=>i(this,void 0,void 0,(function*(){const e=c(t);v=e;const r=yield e;e===v&&(m.validationMessage=r)})))),m.onDidHide((()=>{(()=>i(this,void 0,void 0,(function*(){f(l&&(yield l())?s.resume:s.cancel)})))().catch(f)}))),this.current&&this.current.dispose(),this.current=m,this.current.show()}))}finally{p.forEach((t=>t.dispose()))}}))}}e.MultiStepInput=o},747:t=>{"use strict";t.exports=require("fs")},622:t=>{"use strict";t.exports=require("path")},549:t=>{"use strict";t.exports=require("vscode")}},e={},r=function r(i){var n=e[i];if(void 0!==n)return n.exports;var s=e[i]={exports:{}};return t[i].call(s.exports,s,s.exports,r),s.exports}(112);module.exports=r})(); 3 | //# sourceMappingURL=extension.js.map --------------------------------------------------------------------------------