├── .gitignore ├── LICENSE ├── README.md ├── azure-pipelines.yml ├── icon.png ├── images ├── step_build.png ├── step_install.png └── step_test.png ├── package-lock.json ├── publish.sh ├── sample_project ├── .gitignore ├── .metadata ├── README.md ├── android │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── sampleproject │ │ │ │ └── MainActivity.java │ │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── flutter_export_environment.sh │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ └── contents.xcworkspacedata │ └── Runner │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── main.m ├── junit.xml ├── lib │ └── main.dart ├── pubspec.yaml └── test │ └── widget_test.dart ├── tasks ├── .DS_Store ├── build │ ├── icon.png │ ├── icon.svg │ ├── index.js │ ├── index.ts │ ├── package-lock.json │ ├── package.json │ ├── task.json │ ├── tests │ │ ├── apk.js │ │ └── apk.ts │ ├── tsconfig.json │ └── tslint.json ├── command │ ├── icon.png │ ├── icon.svg │ ├── index.js │ ├── index.ts │ ├── package-lock.json │ ├── package.json │ ├── task.json │ ├── tsconfig.json │ └── tslint.json ├── install │ ├── .taskkey │ ├── icon.png │ ├── icon.svg │ ├── index.js │ ├── index.ts │ ├── package-lock.json │ ├── package.json │ ├── task.json │ ├── tests │ │ ├── latest.js │ │ ├── latest.ts │ │ ├── notexisting.js │ │ └── notexisting.ts │ ├── tsconfig.json │ └── tslint.json └── test │ ├── icon.png │ ├── icon.svg │ ├── index.js │ ├── index.ts │ ├── package-lock.json │ ├── package.json │ ├── task.json │ ├── tests │ ├── basic.js │ └── basic.ts │ ├── tsconfig.json │ └── tslint.json └── vss-extension.json /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/node 3 | 4 | ### Node ### 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | 24 | # nyc test coverage 25 | .nyc_output 26 | 27 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 28 | .grunt 29 | 30 | # Bower dependency directory (https://bower.io/) 31 | bower_components 32 | 33 | # node-waf configuration 34 | .lock-wscript 35 | 36 | # Compiled binary addons (https://nodejs.org/api/addons.html) 37 | build/Release 38 | 39 | # Dependency directories 40 | node_modules/ 41 | jspm_packages/ 42 | 43 | # TypeScript v1 declaration files 44 | typings/ 45 | 46 | # Optional npm cache directory 47 | .npm 48 | 49 | # Optional eslint cache 50 | .eslintcache 51 | 52 | # Optional REPL history 53 | .node_repl_history 54 | 55 | # Output of 'npm pack' 56 | *.tgz 57 | 58 | # Yarn Integrity file 59 | .yarn-integrity 60 | 61 | # dotenv environment variables file 62 | .env 63 | 64 | # parcel-bundler cache (https://parceljs.org/) 65 | .cache 66 | 67 | # next.js build output 68 | .next 69 | 70 | # nuxt.js build output 71 | .nuxt 72 | 73 | # vuepress build output 74 | .vuepress/dist 75 | 76 | # Serverless directories 77 | .serverless 78 | 79 | 80 | # End of https://www.gitignore.io/api/node 81 | 82 | temp 83 | 84 | *.vsix -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Aloïs Deniel 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > As I'm not using Microsoft tools anymore I won't maintain this repository in the future. If someone wants the repository ownership please contact me on [X](https://twitter.com/aloisdeniel). 2 | 3 | # Flutter for Azure DevOps 4 | 5 | [Flutter](http://flutter.io) build task for [Azure DevOps](https://azure.microsoft.com/fr-fr/services/devops/). 6 | 7 | ## Installation 8 | 9 | Installation can be done using [Visual Studio MarketPlace](https://marketplace.visualstudio.com/items?itemName=aloisdeniel.flutter). 10 | 11 | ## Source Code 12 | 13 | Source code can be found on [Github](https://github.com/aloisdeniel/vsts-flutter-tasks). 14 | 15 | ## Usage 16 | 17 | Add the tasks to your build definition. 18 | 19 | ### Install 20 | 21 | ![](images/step_install.png) 22 | 23 | Installs the [Flutter SDK](https://flutter.io/sdk-archive/) onto the running agent if not already installed. Then uses it for following tasks. 24 | 25 | * Select the `channel`: `stable (default)`, `beta`, or `dev`. 26 | * Select the `version` of the SDK to install: `latest (default)`, `custom`. If `custom` is specified, a `custom version` must be set. 27 | * _(Optional)_. Set the `custom version` (in a `..

` semver format) if needed. 28 | 29 | ### Build 30 | 31 | ![](images/step_build.png) 32 | 33 | Build the given mobile application project. You must call the `Flutter Install` task, set a `FlutterToolPath` environment variable, or use the optional Flutter SDK Path task entry that points to your `flutter/bin` folder before execution. All the application bundles are created into the `build/outputs` folder of your project. 34 | 35 | * Select the `project source directory` (that contains to `pubspec.yaml` file). 36 | * Select the `target` platform: `Android (default)`, `iOS`, or `All` for both. 37 | * _(Optional)_. Set `flutter sdk path` if using a local agent with a pre-installed Flutter SDK, can specify the path to utilize it. Otherwise use Flutter Install. 38 | * _(Optional)_. Set `package name` (like `1.2.3`) that will override the manifest's one. 39 | * _(Optional)_. Set `package number` (like `12`) that will override the manifest's one. 40 | * _(Optional)_. Set `build flavour` (like `development`) to specify a build flavour. Must match Android Gradle flavor definition or XCode scheme. 41 | * _(Optional)_. Set `debug` if you wish to override the default release mode for the build. 42 | * _(Optional)_. Set `Additional arguments` if you wish to adds custom arguments to the `build` command. 43 | * __(Android)__._(Optional)_. Set `platform` for the Android target: `android-arm (default)`, `android-arm64`. 44 | * __(iOS)__._(Optional)_. Set `platform` for the iOS target: `device (default)`, `simulator`. 45 | * __(iOS)__._(Optional)_. Codesign the application bundle (only available on device builds, and activated by default). **Warning: you must install a valid certificate before build with the `Install an Apple Certificate`task** 46 | 47 | ### Test 48 | 49 | ![](images/step_test.png) 50 | 51 | Launch tests and publish a report as build test results. 52 | 53 | * Select the `project source directory` (that contains to `pubspec.yaml` file). 54 | * _(Optional)_. Set `test name` as a regular expression matching substrings of the names of tests to run. 55 | * _(Optional)_. Set `Test plain name` as a plain-text substring of the names of tests to run. 56 | * _(Optional)_. Set `Test plain name` as a plain-text substring of the names of tests to run. 57 | * _(Optional)_. Set `update goldens`: whether `matchesGoldenFile()` calls within your test methods should update the golden files rather than test for an existing match. 58 | * _(Optional)_. The number of `concurrent` test processes to run. (defaults to `6`) 59 | 60 | ### command 61 | 62 | Launch a Flutter command with custom arguments. 63 | 64 | ## FAQ 65 | 66 | 67 | > Flutter command isn't recognized ? 68 | 69 | Make sure that you have a `Flutter Install` at the beginning of your definition. 70 | 71 | > Can I run a custom Flutter command ? 72 | 73 | Yes, right after the `Flutter Install` task, a `FlutterToolPath` environment variable points to the `bin` of the Flutter SDK directory. You just have to use `$(FlutterToolPath)` in your following tasks. 74 | 75 | > Can I run Dart program ? 76 | 77 | Yes, actually a Dart runtime is embedded with Flutter tools (in the `/cache/dart-sdk/bin` subdirectory). 78 | 79 | A task example : 80 | 81 | ```yaml 82 | - task: CmdLine@2 83 | displayName: 'Execute Dart program' 84 | inputs: 85 | script: '$(FlutterToolPath)/cache/dart-sdk/bin/dart program.dart arg1 arg2' 86 | workingDirectory: 'src' 87 | ``` 88 | 89 | ## License 90 | 91 | [MIT](https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/master/LICENSE) 92 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Flutter build 2 | variables: 3 | projectDirectory: sample_project 4 | 5 | jobs: 6 | - job: Android 7 | pool: 8 | vmImage: 'macOS-10.13' 9 | steps: 10 | - task: FlutterInstall@0 11 | - task: FlutterBuild@0 12 | inputs: 13 | target: apk 14 | projectDirectory: $(projectDirectory) 15 | - task: FlutterTest@0 16 | inputs: 17 | projectDirectory: $(projectDirectory) 18 | - task: CopyFiles@2 19 | inputs: 20 | contents: '**/*.apk' 21 | targetFolder: '$(build.artifactStagingDirectory)' 22 | - task: PublishBuildArtifacts@1 23 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/icon.png -------------------------------------------------------------------------------- /images/step_build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/images/step_build.png -------------------------------------------------------------------------------- /images/step_install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/images/step_install.png -------------------------------------------------------------------------------- /images/step_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/images/step_test.png -------------------------------------------------------------------------------- /publish.sh: -------------------------------------------------------------------------------- 1 | # Build TS 2 | npm install -g typescript 3 | cd ./tasks/test 4 | npm install 5 | tsc 6 | cd ../install 7 | npm install 8 | tsc 9 | cd ../build 10 | npm install 11 | tsc 12 | cd ../../ 13 | 14 | # Create extension 15 | npm i -g tfx-cli 16 | tfx extension create --manifest-globs vss-extension.json 17 | 18 | # > Upload from https://marketplace.visualstudio.com/manage/publishers/aloisdeniel -------------------------------------------------------------------------------- /sample_project/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.lock 4 | *.log 5 | *.pyc 6 | *.swp 7 | .DS_Store 8 | .atom/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # Visual Studio Code related 20 | .vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | **/doc/api/ 24 | .dart_tool/ 25 | .flutter-plugins 26 | .packages 27 | .pub-cache/ 28 | .pub/ 29 | build/ 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | 40 | # iOS/XCode related 41 | **/ios/**/*.mode1v3 42 | **/ios/**/*.mode2v3 43 | **/ios/**/*.moved-aside 44 | **/ios/**/*.pbxuser 45 | **/ios/**/*.perspectivev3 46 | **/ios/**/*sync/ 47 | **/ios/**/.sconsign.dblite 48 | **/ios/**/.tags* 49 | **/ios/**/.vagrant/ 50 | **/ios/**/DerivedData/ 51 | **/ios/**/Icon? 52 | **/ios/**/Pods/ 53 | **/ios/**/.symlinks/ 54 | **/ios/**/profile 55 | **/ios/**/xcuserdata 56 | **/ios/.generated/ 57 | **/ios/Flutter/App.framework 58 | **/ios/Flutter/Flutter.framework 59 | **/ios/Flutter/Generated.xcconfig 60 | **/ios/Flutter/app.flx 61 | **/ios/Flutter/app.zip 62 | **/ios/Flutter/flutter_assets/ 63 | **/ios/ServiceDefinitions.json 64 | **/ios/Runner/GeneratedPluginRegistrant.* 65 | 66 | # Exceptions to above rules. 67 | !**/ios/**/default.mode1v3 68 | !**/ios/**/default.mode2v3 69 | !**/ios/**/default.pbxuser 70 | !**/ios/**/default.perspectivev3 71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 72 | -------------------------------------------------------------------------------- /sample_project/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: a68b03e9cefa549d70885fe5390dee9d2c0ad30b 8 | channel: dev 9 | -------------------------------------------------------------------------------- /sample_project/README.md: -------------------------------------------------------------------------------- 1 | # sample_project 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | For help getting started with Flutter, view our online 8 | [documentation](https://flutter.io/). 9 | -------------------------------------------------------------------------------- /sample_project/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 27 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.example.sampleproject" 37 | minSdkVersion 16 38 | targetSdkVersion 27 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | testImplementation 'junit:junit:4.12' 59 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 60 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 61 | } 62 | -------------------------------------------------------------------------------- /sample_project/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 8 | 9 | 10 | 15 | 19 | 26 | 30 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /sample_project/android/app/src/main/java/com/example/sampleproject/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.sampleproject; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sample_project/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /sample_project/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample_project/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample_project/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample_project/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample_project/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample_project/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /sample_project/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.1.2' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /sample_project/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | -------------------------------------------------------------------------------- /sample_project/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /sample_project/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /sample_project/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /sample_project/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /sample_project/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /sample_project/ios/Flutter/flutter_export_environment.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a generated file; do not edit or check into version control. 3 | export "FLUTTER_ROOT=/Users/alois/Sources/flutter" 4 | export "FLUTTER_APPLICATION_PATH=/Users/alois/Sources/vsts-flutter-tasks/sample_project" 5 | export "FLUTTER_TARGET=lib/main.dart" 6 | export "FLUTTER_BUILD_DIR=build" 7 | export "SYMROOT=${SOURCE_ROOT}/../build/ios" 8 | export "OTHER_LDFLAGS=$(inherited) -framework Flutter" 9 | export "FLUTTER_FRAMEWORK_DIR=/Users/alois/Sources/flutter/bin/cache/artifacts/engine/ios" 10 | export "FLUTTER_BUILD_NAME=1.0.0" 11 | export "FLUTTER_BUILD_NUMBER=1" 12 | -------------------------------------------------------------------------------- /sample_project/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 14 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 15 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 16 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 17 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 18 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 19 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 20 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 21 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 22 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 23 | /* End PBXBuildFile section */ 24 | 25 | /* Begin PBXCopyFilesBuildPhase section */ 26 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 27 | isa = PBXCopyFilesBuildPhase; 28 | buildActionMask = 2147483647; 29 | dstPath = ""; 30 | dstSubfolderSpec = 10; 31 | files = ( 32 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 33 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 34 | ); 35 | name = "Embed Frameworks"; 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXCopyFilesBuildPhase section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 42 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 43 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; 44 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 45 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 46 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 47 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 48 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 49 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 50 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 51 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 52 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 53 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 54 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 55 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 56 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 57 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 66 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 67 | ); 68 | runOnlyForDeploymentPostprocessing = 0; 69 | }; 70 | /* End PBXFrameworksBuildPhase section */ 71 | 72 | /* Begin PBXGroup section */ 73 | 9740EEB11CF90186004384FC /* Flutter */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, 77 | 3B80C3931E831B6300D905FE /* App.framework */, 78 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 79 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 80 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 81 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 82 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 83 | ); 84 | name = Flutter; 85 | sourceTree = ""; 86 | }; 87 | 97C146E51CF9000F007C117D = { 88 | isa = PBXGroup; 89 | children = ( 90 | 9740EEB11CF90186004384FC /* Flutter */, 91 | 97C146F01CF9000F007C117D /* Runner */, 92 | 97C146EF1CF9000F007C117D /* Products */, 93 | CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, 94 | ); 95 | sourceTree = ""; 96 | }; 97 | 97C146EF1CF9000F007C117D /* Products */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 97C146EE1CF9000F007C117D /* Runner.app */, 101 | ); 102 | name = Products; 103 | sourceTree = ""; 104 | }; 105 | 97C146F01CF9000F007C117D /* Runner */ = { 106 | isa = PBXGroup; 107 | children = ( 108 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 109 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 110 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 111 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 112 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 113 | 97C147021CF9000F007C117D /* Info.plist */, 114 | 97C146F11CF9000F007C117D /* Supporting Files */, 115 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 116 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 117 | ); 118 | path = Runner; 119 | sourceTree = ""; 120 | }; 121 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 97C146F21CF9000F007C117D /* main.m */, 125 | ); 126 | name = "Supporting Files"; 127 | sourceTree = ""; 128 | }; 129 | /* End PBXGroup section */ 130 | 131 | /* Begin PBXNativeTarget section */ 132 | 97C146ED1CF9000F007C117D /* Runner */ = { 133 | isa = PBXNativeTarget; 134 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 135 | buildPhases = ( 136 | 9740EEB61CF901F6004384FC /* Run Script */, 137 | 97C146EA1CF9000F007C117D /* Sources */, 138 | 97C146EB1CF9000F007C117D /* Frameworks */, 139 | 97C146EC1CF9000F007C117D /* Resources */, 140 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 141 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 142 | ); 143 | buildRules = ( 144 | ); 145 | dependencies = ( 146 | ); 147 | name = Runner; 148 | productName = Runner; 149 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 150 | productType = "com.apple.product-type.application"; 151 | }; 152 | /* End PBXNativeTarget section */ 153 | 154 | /* Begin PBXProject section */ 155 | 97C146E61CF9000F007C117D /* Project object */ = { 156 | isa = PBXProject; 157 | attributes = { 158 | LastUpgradeCheck = 0910; 159 | ORGANIZATIONNAME = "The Chromium Authors"; 160 | TargetAttributes = { 161 | 97C146ED1CF9000F007C117D = { 162 | CreatedOnToolsVersion = 7.3.1; 163 | }; 164 | }; 165 | }; 166 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 167 | compatibilityVersion = "Xcode 3.2"; 168 | developmentRegion = English; 169 | hasScannedForEncodings = 0; 170 | knownRegions = ( 171 | en, 172 | Base, 173 | ); 174 | mainGroup = 97C146E51CF9000F007C117D; 175 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 176 | projectDirPath = ""; 177 | projectRoot = ""; 178 | targets = ( 179 | 97C146ED1CF9000F007C117D /* Runner */, 180 | ); 181 | }; 182 | /* End PBXProject section */ 183 | 184 | /* Begin PBXResourcesBuildPhase section */ 185 | 97C146EC1CF9000F007C117D /* Resources */ = { 186 | isa = PBXResourcesBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 190 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 191 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 192 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 193 | 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 194 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | }; 198 | /* End PBXResourcesBuildPhase section */ 199 | 200 | /* Begin PBXShellScriptBuildPhase section */ 201 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputPaths = ( 207 | ); 208 | name = "Thin Binary"; 209 | outputPaths = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | shellPath = /bin/sh; 213 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 214 | }; 215 | 9740EEB61CF901F6004384FC /* Run Script */ = { 216 | isa = PBXShellScriptBuildPhase; 217 | buildActionMask = 2147483647; 218 | files = ( 219 | ); 220 | inputPaths = ( 221 | ); 222 | name = "Run Script"; 223 | outputPaths = ( 224 | ); 225 | runOnlyForDeploymentPostprocessing = 0; 226 | shellPath = /bin/sh; 227 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 228 | }; 229 | /* End PBXShellScriptBuildPhase section */ 230 | 231 | /* Begin PBXSourcesBuildPhase section */ 232 | 97C146EA1CF9000F007C117D /* Sources */ = { 233 | isa = PBXSourcesBuildPhase; 234 | buildActionMask = 2147483647; 235 | files = ( 236 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 237 | 97C146F31CF9000F007C117D /* main.m in Sources */, 238 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 239 | ); 240 | runOnlyForDeploymentPostprocessing = 0; 241 | }; 242 | /* End PBXSourcesBuildPhase section */ 243 | 244 | /* Begin PBXVariantGroup section */ 245 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 246 | isa = PBXVariantGroup; 247 | children = ( 248 | 97C146FB1CF9000F007C117D /* Base */, 249 | ); 250 | name = Main.storyboard; 251 | sourceTree = ""; 252 | }; 253 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 254 | isa = PBXVariantGroup; 255 | children = ( 256 | 97C147001CF9000F007C117D /* Base */, 257 | ); 258 | name = LaunchScreen.storyboard; 259 | sourceTree = ""; 260 | }; 261 | /* End PBXVariantGroup section */ 262 | 263 | /* Begin XCBuildConfiguration section */ 264 | 97C147031CF9000F007C117D /* Debug */ = { 265 | isa = XCBuildConfiguration; 266 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 267 | buildSettings = { 268 | ALWAYS_SEARCH_USER_PATHS = NO; 269 | CLANG_ANALYZER_NONNULL = YES; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 271 | CLANG_CXX_LIBRARY = "libc++"; 272 | CLANG_ENABLE_MODULES = YES; 273 | CLANG_ENABLE_OBJC_ARC = YES; 274 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 275 | CLANG_WARN_BOOL_CONVERSION = YES; 276 | CLANG_WARN_COMMA = YES; 277 | CLANG_WARN_CONSTANT_CONVERSION = YES; 278 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INFINITE_RECURSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 284 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 285 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 286 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 287 | CLANG_WARN_STRICT_PROTOTYPES = YES; 288 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 289 | CLANG_WARN_UNREACHABLE_CODE = YES; 290 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 291 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 292 | COPY_PHASE_STRIP = NO; 293 | DEBUG_INFORMATION_FORMAT = dwarf; 294 | ENABLE_STRICT_OBJC_MSGSEND = YES; 295 | ENABLE_TESTABILITY = YES; 296 | GCC_C_LANGUAGE_STANDARD = gnu99; 297 | GCC_DYNAMIC_NO_PIC = NO; 298 | GCC_NO_COMMON_BLOCKS = YES; 299 | GCC_OPTIMIZATION_LEVEL = 0; 300 | GCC_PREPROCESSOR_DEFINITIONS = ( 301 | "DEBUG=1", 302 | "$(inherited)", 303 | ); 304 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 305 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 306 | GCC_WARN_UNDECLARED_SELECTOR = YES; 307 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 308 | GCC_WARN_UNUSED_FUNCTION = YES; 309 | GCC_WARN_UNUSED_VARIABLE = YES; 310 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 311 | MTL_ENABLE_DEBUG_INFO = YES; 312 | ONLY_ACTIVE_ARCH = YES; 313 | SDKROOT = iphoneos; 314 | TARGETED_DEVICE_FAMILY = "1,2"; 315 | }; 316 | name = Debug; 317 | }; 318 | 97C147041CF9000F007C117D /* Release */ = { 319 | isa = XCBuildConfiguration; 320 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 321 | buildSettings = { 322 | ALWAYS_SEARCH_USER_PATHS = NO; 323 | CLANG_ANALYZER_NONNULL = YES; 324 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 325 | CLANG_CXX_LIBRARY = "libc++"; 326 | CLANG_ENABLE_MODULES = YES; 327 | CLANG_ENABLE_OBJC_ARC = YES; 328 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 329 | CLANG_WARN_BOOL_CONVERSION = YES; 330 | CLANG_WARN_COMMA = YES; 331 | CLANG_WARN_CONSTANT_CONVERSION = YES; 332 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 339 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 340 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 341 | CLANG_WARN_STRICT_PROTOTYPES = YES; 342 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 343 | CLANG_WARN_UNREACHABLE_CODE = YES; 344 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 345 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 346 | COPY_PHASE_STRIP = NO; 347 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 348 | ENABLE_NS_ASSERTIONS = NO; 349 | ENABLE_STRICT_OBJC_MSGSEND = YES; 350 | GCC_C_LANGUAGE_STANDARD = gnu99; 351 | GCC_NO_COMMON_BLOCKS = YES; 352 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 353 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 354 | GCC_WARN_UNDECLARED_SELECTOR = YES; 355 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 356 | GCC_WARN_UNUSED_FUNCTION = YES; 357 | GCC_WARN_UNUSED_VARIABLE = YES; 358 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 359 | MTL_ENABLE_DEBUG_INFO = NO; 360 | SDKROOT = iphoneos; 361 | TARGETED_DEVICE_FAMILY = "1,2"; 362 | VALIDATE_PRODUCT = YES; 363 | }; 364 | name = Release; 365 | }; 366 | 97C147061CF9000F007C117D /* Debug */ = { 367 | isa = XCBuildConfiguration; 368 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 369 | buildSettings = { 370 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 371 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 372 | ENABLE_BITCODE = NO; 373 | FRAMEWORK_SEARCH_PATHS = ( 374 | "$(inherited)", 375 | "$(PROJECT_DIR)/Flutter", 376 | ); 377 | INFOPLIST_FILE = Runner/Info.plist; 378 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 379 | LIBRARY_SEARCH_PATHS = ( 380 | "$(inherited)", 381 | "$(PROJECT_DIR)/Flutter", 382 | ); 383 | PRODUCT_BUNDLE_IDENTIFIER = com.example.sampleProject; 384 | PRODUCT_NAME = "$(TARGET_NAME)"; 385 | VERSIONING_SYSTEM = "apple-generic"; 386 | }; 387 | name = Debug; 388 | }; 389 | 97C147071CF9000F007C117D /* Release */ = { 390 | isa = XCBuildConfiguration; 391 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 392 | buildSettings = { 393 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 394 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 395 | ENABLE_BITCODE = NO; 396 | FRAMEWORK_SEARCH_PATHS = ( 397 | "$(inherited)", 398 | "$(PROJECT_DIR)/Flutter", 399 | ); 400 | INFOPLIST_FILE = Runner/Info.plist; 401 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 402 | LIBRARY_SEARCH_PATHS = ( 403 | "$(inherited)", 404 | "$(PROJECT_DIR)/Flutter", 405 | ); 406 | PRODUCT_BUNDLE_IDENTIFIER = com.example.sampleProject; 407 | PRODUCT_NAME = "$(TARGET_NAME)"; 408 | VERSIONING_SYSTEM = "apple-generic"; 409 | }; 410 | name = Release; 411 | }; 412 | /* End XCBuildConfiguration section */ 413 | 414 | /* Begin XCConfigurationList section */ 415 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 416 | isa = XCConfigurationList; 417 | buildConfigurations = ( 418 | 97C147031CF9000F007C117D /* Debug */, 419 | 97C147041CF9000F007C117D /* Release */, 420 | ); 421 | defaultConfigurationIsVisible = 0; 422 | defaultConfigurationName = Release; 423 | }; 424 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 425 | isa = XCConfigurationList; 426 | buildConfigurations = ( 427 | 97C147061CF9000F007C117D /* Debug */, 428 | 97C147071CF9000F007C117D /* Release */, 429 | ); 430 | defaultConfigurationIsVisible = 0; 431 | defaultConfigurationName = Release; 432 | }; 433 | /* End XCConfigurationList section */ 434 | }; 435 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 436 | } 437 | -------------------------------------------------------------------------------- /sample_project/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample_project/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /sample_project/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sample_project/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /sample_project/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/sample_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /sample_project/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /sample_project/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 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 | -------------------------------------------------------------------------------- /sample_project/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /sample_project/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | sample_project 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /sample_project/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /sample_project/junit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /sample_project/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | void main() => runApp(new MyApp()); 4 | 5 | class MyApp extends StatelessWidget { 6 | // This widget is the root of your application. 7 | @override 8 | Widget build(BuildContext context) { 9 | return new MaterialApp( 10 | title: 'Flutter Demo', 11 | theme: new ThemeData( 12 | // This is the theme of your application. 13 | // 14 | // Try running your application with "flutter run". You'll see the 15 | // application has a blue toolbar. Then, without quitting the app, try 16 | // changing the primarySwatch below to Colors.green and then invoke 17 | // "hot reload" (press "r" in the console where you ran "flutter run", 18 | // or press Run > Flutter Hot Reload in IntelliJ). Notice that the 19 | // counter didn't reset back to zero; the application is not restarted. 20 | primarySwatch: Colors.blue, 21 | ), 22 | home: new MyHomePage(title: 'Flutter Demo Home Page'), 23 | ); 24 | } 25 | } 26 | 27 | class MyHomePage extends StatefulWidget { 28 | MyHomePage({Key key, this.title}) : super(key: key); 29 | 30 | // This widget is the home page of your application. It is stateful, meaning 31 | // that it has a State object (defined below) that contains fields that affect 32 | // how it looks. 33 | 34 | // This class is the configuration for the state. It holds the values (in this 35 | // case the title) provided by the parent (in this case the App widget) and 36 | // used by the build method of the State. Fields in a Widget subclass are 37 | // always marked "final". 38 | 39 | final String title; 40 | 41 | @override 42 | _MyHomePageState createState() => new _MyHomePageState(); 43 | } 44 | 45 | class _MyHomePageState extends State { 46 | int _counter = 0; 47 | 48 | void _incrementCounter() { 49 | setState(() { 50 | // This call to setState tells the Flutter framework that something has 51 | // changed in this State, which causes it to rerun the build method below 52 | // so that the display can reflect the updated values. If we changed 53 | // _counter without calling setState(), then the build method would not be 54 | // called again, and so nothing would appear to happen. 55 | _counter++; 56 | }); 57 | } 58 | 59 | @override 60 | Widget build(BuildContext context) { 61 | // This method is rerun every time setState is called, for instance as done 62 | // by the _incrementCounter method above. 63 | // 64 | // The Flutter framework has been optimized to make rerunning build methods 65 | // fast, so that you can just rebuild anything that needs updating rather 66 | // than having to individually change instances of widgets. 67 | return new Scaffold( 68 | appBar: new AppBar( 69 | // Here we take the value from the MyHomePage object that was created by 70 | // the App.build method, and use it to set our appbar title. 71 | title: new Text(widget.title), 72 | ), 73 | body: new Center( 74 | // Center is a layout widget. It takes a single child and positions it 75 | // in the middle of the parent. 76 | child: new Column( 77 | // Column is also layout widget. It takes a list of children and 78 | // arranges them vertically. By default, it sizes itself to fit its 79 | // children horizontally, and tries to be as tall as its parent. 80 | // 81 | // Invoke "debug paint" (press "p" in the console where you ran 82 | // "flutter run", or select "Toggle Debug Paint" from the Flutter tool 83 | // window in IntelliJ) to see the wireframe for each widget. 84 | // 85 | // Column has various properties to control how it sizes itself and 86 | // how it positions its children. Here we use mainAxisAlignment to 87 | // center the children vertically; the main axis here is the vertical 88 | // axis because Columns are vertical (the cross axis would be 89 | // horizontal). 90 | mainAxisAlignment: MainAxisAlignment.center, 91 | children: [ 92 | new Text( 93 | 'You have pushed the button this many times:', 94 | ), 95 | new Text( 96 | '$_counter', 97 | style: Theme.of(context).textTheme.display1, 98 | ), 99 | ], 100 | ), 101 | ), 102 | floatingActionButton: new FloatingActionButton( 103 | onPressed: _incrementCounter, 104 | tooltip: 'Increment', 105 | child: new Icon(Icons.add), 106 | ), // This trailing comma makes auto-formatting nicer for build methods. 107 | ); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /sample_project/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: sample_project 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # Read more about versioning at semver.org. 10 | version: 1.0.0+1 11 | 12 | environment: 13 | sdk: ">=2.0.0-dev.68.0 <3.0.0" 14 | 15 | dependencies: 16 | flutter: 17 | sdk: flutter 18 | 19 | # The following adds the Cupertino Icons font to your application. 20 | # Use with the CupertinoIcons class for iOS style icons. 21 | cupertino_icons: ^0.1.2 22 | 23 | dev_dependencies: 24 | flutter_test: 25 | sdk: flutter 26 | 27 | 28 | # For information on the generic Dart part of this file, see the 29 | # following page: https://www.dartlang.org/tools/pub/pubspec 30 | 31 | # The following section is specific to Flutter. 32 | flutter: 33 | 34 | # The following line ensures that the Material Icons font is 35 | # included with your application, so that you can use the icons in 36 | # the material Icons class. 37 | uses-material-design: true 38 | 39 | # To add assets to your application, add an assets section, like this: 40 | # assets: 41 | # - images/a_dot_burr.jpeg 42 | # - images/a_dot_ham.jpeg 43 | 44 | # An image asset can refer to one or more resolution-specific "variants", see 45 | # https://flutter.io/assets-and-images/#resolution-aware. 46 | 47 | # For details regarding adding assets from package dependencies, see 48 | # https://flutter.io/assets-and-images/#from-packages 49 | 50 | # To add custom fonts to your application, add a fonts section here, 51 | # in this "flutter" section. Each entry in this list should have a 52 | # "family" key with the font family name, and a "fonts" key with a 53 | # list giving the asset and other descriptors for the font. For 54 | # example: 55 | # fonts: 56 | # - family: Schyler 57 | # fonts: 58 | # - asset: fonts/Schyler-Regular.ttf 59 | # - asset: fonts/Schyler-Italic.ttf 60 | # style: italic 61 | # - family: Trajan Pro 62 | # fonts: 63 | # - asset: fonts/TrajanPro.ttf 64 | # - asset: fonts/TrajanPro_Bold.ttf 65 | # weight: 700 66 | # 67 | # For details regarding fonts from package dependencies, 68 | # see https://flutter.io/custom-fonts/#from-packages 69 | -------------------------------------------------------------------------------- /sample_project/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // To perform an interaction with a widget in your test, use the WidgetTester utility that Flutter 3 | // provides. For example, you can send tap and scroll gestures. You can also use WidgetTester to 4 | // find child widgets in the widget tree, read text, and verify that the values of widget properties 5 | // are correct. 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter_test/flutter_test.dart'; 9 | 10 | import 'package:sample_project/main.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(new MyApp()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | testWidgets('Other test', (WidgetTester tester) async { 30 | 31 | }); 32 | testWidgets('And another test', (WidgetTester tester) async { 33 | //throw Error(); 34 | }); 35 | } 36 | -------------------------------------------------------------------------------- /tasks/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/tasks/.DS_Store -------------------------------------------------------------------------------- /tasks/build/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/tasks/build/icon.png -------------------------------------------------------------------------------- /tasks/build/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 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 | -------------------------------------------------------------------------------- /tasks/build/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const path = require("path"); 13 | const task = require("azure-pipelines-task-lib/task"); 14 | const FLUTTER_TOOL_PATH_ENV_VAR = 'FlutterToolPath'; 15 | function main() { 16 | return __awaiter(this, void 0, void 0, function* () { 17 | // 1. Check flutter environment 18 | var flutterPath = task.getVariable(FLUTTER_TOOL_PATH_ENV_VAR) || process.env[FLUTTER_TOOL_PATH_ENV_VAR] || task.getInput('flutterDirectory', false); 19 | flutterPath = path.join(flutterPath, "flutter"); 20 | if (!flutterPath) { 21 | throw new Error(`The '${FLUTTER_TOOL_PATH_ENV_VAR}' environment variable must be set before using this task (you can use 'flutterinstall' task).`); 22 | } 23 | // 2. Get target 24 | let target = task.getInput('target', true); 25 | // 3. Move current working directory to project 26 | let projectDirectory = task.getPathInput('projectDirectory', false, false); 27 | if (projectDirectory) { 28 | task.debug(`Moving to ${projectDirectory}`); 29 | task.cd(projectDirectory); 30 | } 31 | task.debug(`Project's directory : ${task.cwd()}`); 32 | // 4. Get common input 33 | let debugMode = task.getBoolInput('debugMode', false); 34 | let buildName = task.getInput('buildName', false); 35 | let buildNumber = task.getInput('buildNumber', false); 36 | let buildFlavour = task.getInput('buildFlavour', false); 37 | let entryPoint = task.getInput('entryPoint', false); 38 | let additionalArguments = task.getInput('additionalArguments', false); 39 | // 5. Builds 40 | if (target === "all" || target === "ios") { 41 | let targetPlatform = task.getInput('iosTargetPlatform', false); 42 | let codesign = task.getBoolInput('iosCodesign', false); 43 | yield buildIpa(flutterPath, additionalArguments, targetPlatform == "simulator", codesign, buildName, buildNumber, debugMode, buildFlavour, entryPoint); 44 | } 45 | if (target === "all" || target === "apk") { 46 | let targetPlatform = task.getInput('apkTargetPlatform', false); 47 | yield buildApk(flutterPath, additionalArguments, targetPlatform, buildName, buildNumber, debugMode, buildFlavour, entryPoint); 48 | } 49 | if (target === "all" || target === "aab") { 50 | yield buildAab(flutterPath, additionalArguments, buildName, buildNumber, debugMode, buildFlavour, entryPoint); 51 | } 52 | if (target === "web") { 53 | yield buildWeb(flutterPath, additionalArguments); 54 | } 55 | task.setResult(task.TaskResult.Succeeded, "Application built"); 56 | }); 57 | } 58 | function buildApk(flutter, additionalArguments, targetPlatform, buildName, buildNumber, debugMode, buildFlavour, entryPoint) { 59 | return __awaiter(this, void 0, void 0, function* () { 60 | var args = [ 61 | "build", 62 | "apk" 63 | ]; 64 | if (debugMode) { 65 | args.push("--debug"); 66 | } 67 | if (targetPlatform) { 68 | args.push("--target-platform=" + targetPlatform); 69 | } 70 | if (buildName) { 71 | args.push("--build-name=" + buildName); 72 | } 73 | if (buildNumber) { 74 | args.push("--build-number=" + buildNumber); 75 | } 76 | if (buildFlavour) { 77 | args.push("--flavor=" + buildFlavour); 78 | } 79 | if (entryPoint) { 80 | args.push("--target=" + entryPoint); 81 | } 82 | if (additionalArguments) { 83 | var splitted = additionalArguments.split(" "); 84 | args.push(...splitted); 85 | } 86 | var result = yield task.exec(flutter, args); 87 | if (result !== 0) { 88 | throw new Error("apk build failed"); 89 | } 90 | }); 91 | } 92 | function buildAab(flutter, additionalArguments, buildName, buildNumber, debugMode, buildFlavour, entryPoint) { 93 | return __awaiter(this, void 0, void 0, function* () { 94 | var args = [ 95 | "build", 96 | "appbundle" 97 | ]; 98 | if (debugMode) { 99 | args.push("--debug"); 100 | } 101 | if (buildName) { 102 | args.push("--build-name=" + buildName); 103 | } 104 | if (buildNumber) { 105 | args.push("--build-number=" + buildNumber); 106 | } 107 | if (buildFlavour) { 108 | args.push("--flavor=" + buildFlavour); 109 | } 110 | if (entryPoint) { 111 | args.push("--target=" + entryPoint); 112 | } 113 | if (additionalArguments) { 114 | var splitted = additionalArguments.split(" "); 115 | args.push(...splitted); 116 | } 117 | var result = yield task.exec(flutter, args); 118 | if (result !== 0) { 119 | throw new Error("aab build failed"); 120 | } 121 | }); 122 | } 123 | function buildIpa(flutter, additionalArguments, simulator, codesign, buildName, buildNumber, debugMode, buildFlavour, entryPoint) { 124 | return __awaiter(this, void 0, void 0, function* () { 125 | var args = [ 126 | "build", 127 | "ios" 128 | ]; 129 | if (debugMode) { 130 | args.push("--debug"); 131 | } 132 | if (simulator) { 133 | args.push("--simulator"); 134 | if (!debugMode) { 135 | args.push("--debug"); // simulator can only be built in debug 136 | } 137 | } 138 | else if (codesign) { 139 | args.push("--codesign"); 140 | } 141 | else { 142 | args.push("--no-codesign"); 143 | } 144 | if (buildName) { 145 | args.push("--build-name=" + buildName); 146 | } 147 | if (buildNumber) { 148 | args.push("--build-number=" + buildNumber); 149 | } 150 | if (entryPoint) { 151 | args.push("--target=" + entryPoint); 152 | } 153 | if (!simulator) { 154 | if (buildFlavour) { 155 | args.push("--flavor=" + buildFlavour); 156 | } 157 | } 158 | if (additionalArguments) { 159 | var splitted = additionalArguments.split(" "); 160 | args.push(...splitted); 161 | } 162 | var result = yield task.exec(flutter, args); 163 | if (result !== 0) { 164 | throw new Error("ios build failed"); 165 | } 166 | }); 167 | } 168 | function buildWeb(flutter, additionalArguments) { 169 | return __awaiter(this, void 0, void 0, function* () { 170 | var args = [ 171 | "build", 172 | "web" 173 | ]; 174 | if (additionalArguments) { 175 | var splitted = additionalArguments.split(" "); 176 | args.push(...splitted); 177 | } 178 | var result = yield task.exec(flutter, args); 179 | if (result !== 0) { 180 | throw new Error("web build failed"); 181 | } 182 | }); 183 | } 184 | main().catch(error => { 185 | task.setResult(task.TaskResult.Failed, error); 186 | }); 187 | -------------------------------------------------------------------------------- /tasks/build/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as task from "azure-pipelines-task-lib/task"; 3 | 4 | const FLUTTER_TOOL_PATH_ENV_VAR: string = 'FlutterToolPath'; 5 | 6 | async function main(): Promise { 7 | // 1. Check flutter environment 8 | var flutterPath = task.getVariable(FLUTTER_TOOL_PATH_ENV_VAR) || process.env[FLUTTER_TOOL_PATH_ENV_VAR] || task.getInput('flutterDirectory', false); 9 | flutterPath = path.join(flutterPath, "flutter") 10 | if (!flutterPath) { 11 | throw new Error(`The '${FLUTTER_TOOL_PATH_ENV_VAR}' environment variable must be set before using this task (you can use 'flutterinstall' task).`); 12 | } 13 | 14 | // 2. Get target 15 | let target = task.getInput('target', true); 16 | 17 | // 3. Move current working directory to project 18 | let projectDirectory = task.getPathInput('projectDirectory', false, false); 19 | if (projectDirectory) { 20 | task.debug(`Moving to ${projectDirectory}`); 21 | task.cd(projectDirectory); 22 | } 23 | task.debug(`Project's directory : ${task.cwd()}`); 24 | 25 | // 4. Get common input 26 | let debugMode = task.getBoolInput('debugMode', false); 27 | let buildName = task.getInput('buildName', false); 28 | let buildNumber = task.getInput('buildNumber', false); 29 | let buildFlavour = task.getInput('buildFlavour', false); 30 | let entryPoint = task.getInput('entryPoint', false); 31 | let additionalArguments = task.getInput('additionalArguments', false); 32 | 33 | 34 | // 5. Builds 35 | if (target === "all" || target === "ios") { 36 | let targetPlatform = task.getInput('iosTargetPlatform', false); 37 | let codesign = task.getBoolInput('iosCodesign', false); 38 | await buildIpa(flutterPath, additionalArguments, targetPlatform == "simulator", codesign, buildName, buildNumber, debugMode, buildFlavour, entryPoint); 39 | } 40 | 41 | if (target === "all" || target === "apk") { 42 | let targetPlatform = task.getInput('apkTargetPlatform', false); 43 | await buildApk(flutterPath, additionalArguments, targetPlatform, buildName, buildNumber, debugMode, buildFlavour, entryPoint); 44 | } 45 | 46 | if (target === "all" || target === "aab") { 47 | await buildAab(flutterPath, additionalArguments, buildName, buildNumber, debugMode, buildFlavour, entryPoint); 48 | } 49 | 50 | if (target === "web") { 51 | await buildWeb(flutterPath, additionalArguments); 52 | } 53 | 54 | task.setResult(task.TaskResult.Succeeded, "Application built"); 55 | } 56 | 57 | async function buildApk(flutter: string, additionalArguments: string, targetPlatform?: string, buildName?: string, buildNumber?: string, debugMode?: boolean, buildFlavour?: string, entryPoint?: string) { 58 | 59 | var args = [ 60 | "build", 61 | "apk" 62 | ]; 63 | 64 | if (debugMode) { 65 | args.push("--debug"); 66 | } 67 | 68 | if (targetPlatform) { 69 | args.push("--target-platform=" + targetPlatform); 70 | } 71 | 72 | if (buildName) { 73 | args.push("--build-name=" + buildName); 74 | } 75 | 76 | if (buildNumber) { 77 | args.push("--build-number=" + buildNumber); 78 | } 79 | 80 | if (buildFlavour) { 81 | args.push("--flavor=" + buildFlavour); 82 | } 83 | 84 | if (entryPoint) { 85 | args.push("--target=" + entryPoint); 86 | } 87 | 88 | if (additionalArguments) { 89 | var splitted = additionalArguments.split(" "); 90 | args.push(...splitted) 91 | } 92 | 93 | var result = await task.exec(flutter, args); 94 | 95 | if (result !== 0) { 96 | throw new Error("apk build failed"); 97 | } 98 | } 99 | 100 | async function buildAab(flutter: string, additionalArguments: string, buildName?: string, buildNumber?: string, debugMode?: boolean, buildFlavour?: string, entryPoint?: string) { 101 | 102 | var args = [ 103 | "build", 104 | "appbundle" 105 | ]; 106 | 107 | if (debugMode) { 108 | args.push("--debug"); 109 | } 110 | 111 | if (buildName) { 112 | args.push("--build-name=" + buildName); 113 | } 114 | 115 | if (buildNumber) { 116 | args.push("--build-number=" + buildNumber); 117 | } 118 | 119 | if (buildFlavour) { 120 | args.push("--flavor=" + buildFlavour); 121 | } 122 | 123 | if (entryPoint) { 124 | args.push("--target=" + entryPoint); 125 | } 126 | 127 | if (additionalArguments) { 128 | var splitted = additionalArguments.split(" "); 129 | args.push(...splitted) 130 | } 131 | 132 | var result = await task.exec(flutter, args); 133 | 134 | if (result !== 0) { 135 | throw new Error("aab build failed"); 136 | } 137 | } 138 | 139 | async function buildIpa(flutter: string, additionalArguments: string, simulator?: boolean, codesign?: boolean, buildName?: string, buildNumber?: string, debugMode?: boolean, buildFlavour?: string, entryPoint?: string) { 140 | 141 | var args = [ 142 | "build", 143 | "ios" 144 | ]; 145 | 146 | if (debugMode) { 147 | args.push("--debug"); 148 | } 149 | 150 | if (simulator) { 151 | args.push("--simulator"); 152 | 153 | if (!debugMode) { 154 | args.push("--debug"); // simulator can only be built in debug 155 | } 156 | } 157 | else if (codesign) { 158 | args.push("--codesign"); 159 | } 160 | else { 161 | args.push("--no-codesign"); 162 | } 163 | 164 | if (buildName) { 165 | args.push("--build-name=" + buildName); 166 | } 167 | 168 | if (buildNumber) { 169 | args.push("--build-number=" + buildNumber); 170 | } 171 | 172 | if (entryPoint) { 173 | args.push("--target=" + entryPoint); 174 | } 175 | 176 | if (!simulator) { 177 | if (buildFlavour) { 178 | args.push("--flavor=" + buildFlavour); 179 | } 180 | } 181 | 182 | if (additionalArguments) { 183 | var splitted = additionalArguments.split(" "); 184 | args.push(...splitted) 185 | } 186 | 187 | var result = await task.exec(flutter, args); 188 | if (result !== 0) { 189 | throw new Error("ios build failed"); 190 | } 191 | } 192 | 193 | async function buildWeb(flutter: string, additionalArguments: string,) { 194 | 195 | var args = [ 196 | "build", 197 | "web" 198 | ]; 199 | 200 | if (additionalArguments) { 201 | var splitted = additionalArguments.split(" "); 202 | args.push(...splitted) 203 | } 204 | 205 | var result = await task.exec(flutter, args); 206 | 207 | if (result !== 0) { 208 | throw new Error("web build failed"); 209 | } 210 | } 211 | 212 | main().catch(error => { 213 | task.setResult(task.TaskResult.Failed, error); 214 | }); 215 | -------------------------------------------------------------------------------- /tasks/build/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutterbuild", 3 | "version": "0.2.33", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "13.13.5", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.5.tgz", 10 | "integrity": "sha512-3ySmiBYJPqgjiHA7oEaIo2Rzz0HrOZ7yrNO5HWyaE5q0lQ3BppDZ3N53Miz8bw2I7gh1/zir2MGVZBvpb1zq9g==" 11 | }, 12 | "azure-pipelines-task-lib": { 13 | "version": "2.7.7", 14 | "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-2.7.7.tgz", 15 | "integrity": "sha512-4KBPheFTxTDqvaY0bjs9/Ab5yb2c/Y5u8gd54UGL2xhGbv2eoahOZPerAUY/vKsUDu2mjlP/JAWTlDv7dghdRQ==", 16 | "requires": { 17 | "minimatch": "3.0.4", 18 | "mockery": "^1.7.0", 19 | "q": "^1.1.2", 20 | "semver": "^5.1.0", 21 | "shelljs": "^0.3.0", 22 | "uuid": "^3.0.1" 23 | } 24 | }, 25 | "balanced-match": { 26 | "version": "1.0.0", 27 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 28 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 29 | }, 30 | "brace-expansion": { 31 | "version": "1.1.11", 32 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 33 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 34 | "requires": { 35 | "balanced-match": "^1.0.0", 36 | "concat-map": "0.0.1" 37 | } 38 | }, 39 | "concat-map": { 40 | "version": "0.0.1", 41 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 42 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 43 | }, 44 | "minimatch": { 45 | "version": "3.0.4", 46 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 47 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 48 | "requires": { 49 | "brace-expansion": "^1.1.7" 50 | } 51 | }, 52 | "mockery": { 53 | "version": "1.7.0", 54 | "resolved": "https://registry.npmjs.org/mockery/-/mockery-1.7.0.tgz", 55 | "integrity": "sha1-9O3g2HUMHJcnwnLqLGBiniyaHE8=" 56 | }, 57 | "q": { 58 | "version": "1.5.1", 59 | "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", 60 | "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" 61 | }, 62 | "semver": { 63 | "version": "5.5.1", 64 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", 65 | "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" 66 | }, 67 | "shelljs": { 68 | "version": "0.3.0", 69 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", 70 | "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" 71 | }, 72 | "uuid": { 73 | "version": "3.3.2", 74 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 75 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tasks/build/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutterbuild", 3 | "version": "0.2.33", 4 | "description": "Flutter Build Task", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/aloisdeniel/vsts-flutter-tasks.git" 12 | }, 13 | "author": "Aloïs Deniel", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/aloisdeniel/vsts-flutter-tasks/issues" 17 | }, 18 | "homepage": "https://github.com/aloisdeniel/vsts-flutter-tasks#readme", 19 | "dependencies": { 20 | "azure-pipelines-task-lib": "^2.7.0", 21 | "@types/node": "^13.13.5" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tasks/build/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "10a2efb1-b922-4849-81de-bccd2537ccc1", 3 | "name": "FlutterBuild", 4 | "friendlyName": "Flutter Build", 5 | "description": "Build a Flutter application project.", 6 | "helpMarkDown": "Don't forget to insert a 'Flutter Install' task before. [More Information](https://github.com/aloisdeniel/vsts-flutter-tasks)", 7 | "category": "Build", 8 | "visibility": [ 9 | "Build" 10 | ], 11 | "author": "Aloïs Deniel", 12 | "version": { 13 | "Major": 0, 14 | "Minor": 2, 15 | "Patch": 34 16 | }, 17 | "groups": [], 18 | "instanceNameFormat": "Flutter Build $(target)", 19 | "inputs": [ 20 | { 21 | "name": "target", 22 | "type": "pickList", 23 | "label": "Target platform", 24 | "defaultValue": "apk", 25 | "helpMarkDown": "The target platform of your application.", 26 | "required": true, 27 | "options": { 28 | "all": "All (except 'Web').", 29 | "ios": "iOS", 30 | "apk": "Android (apk)", 31 | "aab": "Android (aab)", 32 | "web": "Web" 33 | } 34 | }, 35 | { 36 | "name": "projectDirectory", 37 | "type": "filePath", 38 | "label": "Path to the project folder", 39 | "defaultValue": ".", 40 | "helpMarkDown": "The path to the project folder (which contains the `pubspec.yaml`)", 41 | "required": true 42 | }, 43 | { 44 | "name": "flutterDirectory", 45 | "type": "filePath", 46 | "label": "Path to the Flutter SDK", 47 | "defaultValue": "", 48 | "helpMarkDown": "Path to the Flutter SDK if not using Flutter Install task before this one.", 49 | "required": false 50 | }, 51 | { 52 | "name": "debugMode", 53 | "type": "boolean", 54 | "label": "Debug", 55 | "defaultValue": false, 56 | "helpMarkDown": "Force build type to be Debug instead of Release.", 57 | "required": false 58 | }, 59 | { 60 | "name": "buildFlavour", 61 | "type": "string", 62 | "label": "Build Flavour", 63 | "defaultValue": "", 64 | "helpMarkDown": "The flavour of the build, (e.g. production, development). Must match either an Android Gradle flavour or Xcode scheme.", 65 | "required": false 66 | }, 67 | { 68 | "name": "buildNumber", 69 | "type": "string", 70 | "label": "Package build number", 71 | "defaultValue": "$(Build.BuildNumber)", 72 | "required": false, 73 | "helpMarkDown": "An identifier used as an internal version number. Each build must have a unique identifier to differentiate it from previous builds." 74 | }, 75 | { 76 | "name": "buildName", 77 | "type": "string", 78 | "label": "Version number", 79 | "defaultValue": "", 80 | "required": false, 81 | "helpMarkDown": "An `x.y.z` string used as the version number shown to users. For each new version of your app, you should provide a version number to differentiate it from previous versions." 82 | }, 83 | { 84 | "name": "entryPoint", 85 | "type": "string", 86 | "label": "Entry-point", 87 | "defaultValue": "", 88 | "required": false, 89 | "helpMarkDown": "The main entry-point file of the application, as run on the device. (defaults to 'lib/main.dart')" 90 | }, 91 | { 92 | "name": "apkTargetPlatform", 93 | "type": "pickList", 94 | "label": "Target platform architecture", 95 | "defaultValue": "android-arm", 96 | "required": false, 97 | "helpMarkDown": "Sets the target android platform architecture", 98 | "visibleRule": "target = apk", 99 | "options": { 100 | "android-arm": "Arm", 101 | "android-arm64": "Arm64" 102 | } 103 | }, 104 | { 105 | "name": "iosTargetPlatform", 106 | "type": "pickList", 107 | "label": "Target platform architecture", 108 | "defaultValue": "device", 109 | "required": false, 110 | "helpMarkDown": "Sets the target iOS platform", 111 | "visibleRule": "target = ios", 112 | "options": { 113 | "device": "Device", 114 | "simulator": "Simulator" 115 | } 116 | }, 117 | { 118 | "name": "iosCodesign", 119 | "type": "boolean", 120 | "label": "Codesign application bundle", 121 | "defaultValue": "true", 122 | "required": false, 123 | "helpMarkDown": "Indicates whether the application bundle should be codesigned. **Warning: you must install a valid certificate before build with the `Install an Apple Certificate`task**", 124 | "visibleRule": "target = ios" 125 | }, 126 | { 127 | "name": "additionalArguments", 128 | "type": "string", 129 | "label": "Additional arguments", 130 | "required": false, 131 | "defaultValue": "", 132 | "helpMarkDown": "Additional arguments passed to the flutter build command." 133 | } 134 | ], 135 | "execution": { 136 | "Node": { 137 | "target": "index.js" 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /tasks/build/tests/apk.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | const mr = require("azure-pipelines-task-lib/mock-run"); 6 | const taskPath = path.join(__dirname, "../index.js"); 7 | var runner = new mr.TaskMockRunner(taskPath); 8 | function assertDirectory(path) { 9 | if (!fs.existsSync(path)) { 10 | fs.mkdirSync(path); 11 | } 12 | } 13 | // ENV 14 | const rootPath = path.join(__dirname, "..", "..", ".."); 15 | const tempPath = path.join(rootPath, "temp"); 16 | const agentPath = path.join(tempPath, "agent"); 17 | const dropPath = path.join(tempPath, "drop"); 18 | process.env["BUILD_BUILDNUMBER"] = "1"; 19 | assertDirectory(tempPath); 20 | assertDirectory(agentPath); 21 | assertDirectory(dropPath); 22 | assertDirectory(process.env["AGENT_HOMEDIRECTORY"] = path.join(agentPath, "home")); 23 | assertDirectory(process.env["AGENT_TOOLSDIRECTORY"] = path.join(agentPath, "tools")); 24 | assertDirectory(process.env["AGENT_TEMPDIRECTORY"] = path.join(agentPath, "temp")); 25 | assertDirectory(process.env["AGENT_BUILDDIRECTORY"] = path.join(agentPath, "build")); 26 | // Run install tests 27 | process.env["FlutterToolPath"] = path.join(agentPath, "tools", "Flutter", "0.9.6-dev", "macos", "flutter", "bin"); 28 | runner.setInput("target", "apk"); 29 | runner.setInput("buildName", "com.aloisdeniel.vsts"); 30 | runner.setInput("buildNumber", "12"); 31 | runner.setInput("projectDirectory", path.join(rootPath, "sample_project")); 32 | runner.setInput("outputDirectory", dropPath); 33 | runner.setInput("buildFlavour", "development"); 34 | runner.setInput("debugMode", "false"); 35 | runner.run(true); 36 | -------------------------------------------------------------------------------- /tasks/build/tests/apk.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as fs from 'fs'; 3 | import * as mr from 'azure-pipelines-task-lib/mock-run'; 4 | 5 | const taskPath = path.join(__dirname, "../index.js"); 6 | var runner = new mr.TaskMockRunner(taskPath); 7 | 8 | function assertDirectory(path: string) { 9 | if (!fs.existsSync(path)) { 10 | fs.mkdirSync(path); 11 | } 12 | } 13 | 14 | // ENV 15 | const rootPath = path.join(__dirname, "..", "..", ".."); 16 | const tempPath = path.join(rootPath, "temp"); 17 | const agentPath = path.join(tempPath, "agent"); 18 | const dropPath = path.join(tempPath, "drop"); 19 | process.env["BUILD_BUILDNUMBER"] = "1"; 20 | assertDirectory(tempPath); 21 | assertDirectory(agentPath); 22 | assertDirectory(dropPath); 23 | assertDirectory(process.env["AGENT_HOMEDIRECTORY"] = path.join(agentPath, "home")); 24 | assertDirectory(process.env["AGENT_TOOLSDIRECTORY"] = path.join(agentPath, "tools")); 25 | assertDirectory(process.env["AGENT_TEMPDIRECTORY"] = path.join(agentPath, "temp")); 26 | assertDirectory(process.env["AGENT_BUILDDIRECTORY"] = path.join(agentPath, "build")); 27 | 28 | // Run install tests 29 | process.env["FlutterToolPath"] = path.join(agentPath, "tools", "Flutter", "0.9.6-dev", "macos", "flutter", "bin"); 30 | 31 | runner.setInput("target", "apk"); 32 | runner.setInput("buildName", "com.aloisdeniel.vsts"); 33 | runner.setInput("buildNumber", "12"); 34 | runner.setInput("projectDirectory", path.join(rootPath, "sample_project")); 35 | runner.setInput("outputDirectory", dropPath); 36 | runner.setInput("buildFlavour", "development"); 37 | runner.setInput("debugMode", "false"); 38 | 39 | runner.run(true); -------------------------------------------------------------------------------- /tasks/build/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs" 5 | }, 6 | "exclude": [ 7 | "node_modules" 8 | ] 9 | } -------------------------------------------------------------------------------- /tasks/build/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:latest" 4 | ], 5 | "rules": { 6 | "member-ordering": [ false ], 7 | "interface-name": [ false ], 8 | "object-literal-sort-keys": false, 9 | 10 | "whitespace": [ 11 | "check-branch", 12 | "check-decl", 13 | "check-operator", 14 | "check-module", 15 | "check-separator", 16 | "check-type" 17 | ], 18 | 19 | "one-line": [ 20 | "check-open-brace", 21 | "check-whitespace" 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /tasks/command/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/tasks/command/icon.png -------------------------------------------------------------------------------- /tasks/command/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 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 | -------------------------------------------------------------------------------- /tasks/command/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const task = require("azure-pipelines-task-lib/task"); 13 | const FLUTTER_TOOL_PATH_ENV_VAR = 'FlutterToolPath'; 14 | function main() { 15 | return __awaiter(this, void 0, void 0, function* () { 16 | let args = task.getInput('arguments', false); 17 | let splittedArgs = args.split(' ') 18 | .map(function (x) { 19 | return x.trim(); 20 | }) 21 | .filter(function (x) { 22 | return x.length; 23 | }); 24 | // Move current working directory to project 25 | let projectDirectory = task.getPathInput('projectDirectory', false, false); 26 | if (projectDirectory) { 27 | task.debug(`Moving to ${projectDirectory}`); 28 | task.cd(projectDirectory); 29 | } 30 | task.debug(`Project's directory : ${task.cwd()}`); 31 | var result = yield task.exec(FLUTTER_TOOL_PATH_ENV_VAR, splittedArgs); 32 | if (result !== 0) { 33 | task.setResult(task.TaskResult.Failed, "Command execution failed"); 34 | } 35 | else { 36 | task.setResult(task.TaskResult.Succeeded, "Command execution succeeded"); 37 | } 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /tasks/command/index.ts: -------------------------------------------------------------------------------- 1 | import * as task from "azure-pipelines-task-lib/task"; 2 | 3 | const FLUTTER_TOOL_PATH_ENV_VAR: string = 'FlutterToolPath'; 4 | 5 | async function main(): Promise { 6 | let args = task.getInput('arguments', false); 7 | let splittedArgs = args.split(' ') 8 | .map(function (x) { 9 | return x.trim(); 10 | }) 11 | .filter(function (x) { 12 | return x.length; 13 | }); 14 | 15 | // Move current working directory to project 16 | let projectDirectory = task.getPathInput('projectDirectory', false, false); 17 | if (projectDirectory) { 18 | task.debug(`Moving to ${projectDirectory}`); 19 | task.cd(projectDirectory); 20 | } 21 | task.debug(`Project's directory : ${task.cwd()}`); 22 | 23 | var result = await task.exec(FLUTTER_TOOL_PATH_ENV_VAR, splittedArgs); 24 | 25 | if (result !== 0) { 26 | task.setResult(task.TaskResult.Failed, "Command execution failed"); 27 | } 28 | else { 29 | task.setResult(task.TaskResult.Succeeded, "Command execution succeeded"); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /tasks/command/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fluttercommand", 3 | "version": "0.2.32", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "13.13.5", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.5.tgz", 10 | "integrity": "sha512-3ySmiBYJPqgjiHA7oEaIo2Rzz0HrOZ7yrNO5HWyaE5q0lQ3BppDZ3N53Miz8bw2I7gh1/zir2MGVZBvpb1zq9g==" 11 | }, 12 | "azure-pipelines-task-lib": { 13 | "version": "2.9.3", 14 | "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-2.9.3.tgz", 15 | "integrity": "sha512-SPWKSfgmjyBDVIMzXnnPH0Gv7YXZ+AQ3SyIhNNALAmQpOltqJhgslvzrOClR5rKuoOyJlG0AWZILbZIXCkztAA==", 16 | "requires": { 17 | "minimatch": "3.0.4", 18 | "mockery": "^1.7.0", 19 | "q": "^1.1.2", 20 | "semver": "^5.1.0", 21 | "shelljs": "^0.3.0", 22 | "uuid": "^3.0.1" 23 | } 24 | }, 25 | "balanced-match": { 26 | "version": "1.0.0", 27 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 28 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 29 | }, 30 | "brace-expansion": { 31 | "version": "1.1.11", 32 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 33 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 34 | "requires": { 35 | "balanced-match": "^1.0.0", 36 | "concat-map": "0.0.1" 37 | } 38 | }, 39 | "concat-map": { 40 | "version": "0.0.1", 41 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 42 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 43 | }, 44 | "minimatch": { 45 | "version": "3.0.4", 46 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 47 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 48 | "requires": { 49 | "brace-expansion": "^1.1.7" 50 | } 51 | }, 52 | "mockery": { 53 | "version": "1.7.0", 54 | "resolved": "https://registry.npmjs.org/mockery/-/mockery-1.7.0.tgz", 55 | "integrity": "sha1-9O3g2HUMHJcnwnLqLGBiniyaHE8=" 56 | }, 57 | "q": { 58 | "version": "1.5.1", 59 | "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", 60 | "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" 61 | }, 62 | "semver": { 63 | "version": "5.7.1", 64 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", 65 | "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" 66 | }, 67 | "shelljs": { 68 | "version": "0.3.0", 69 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", 70 | "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" 71 | }, 72 | "uuid": { 73 | "version": "3.4.0", 74 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 75 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /tasks/command/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fluttercommand", 3 | "version": "0.2.32", 4 | "description": "Flutter Command Task", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/aloisdeniel/vsts-flutter-tasks.git" 12 | }, 13 | "author": "Aloïs Deniel", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/aloisdeniel/vsts-flutter-tasks/issues" 17 | }, 18 | "homepage": "https://github.com/aloisdeniel/vsts-flutter-tasks#readme", 19 | "dependencies": { 20 | "azure-pipelines-task-lib": "^2.7.0", 21 | "@types/node": "^13.13.5" 22 | } 23 | } -------------------------------------------------------------------------------- /tasks/command/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "d885c1e4-9aa1-4a16-b2ec-07db402304d4", 3 | "name": "FlutterCommand", 4 | "friendlyName": "Flutter Command", 5 | "description": "Launch a custom Flutter command with custom arguments.", 6 | "helpMarkDown": "Don't forget to insert a 'Flutter Install' task before. [More Information](https://github.com/aloisdeniel/vsts-flutter-tasks)", 7 | "category": "Build", 8 | "visibility": [ 9 | "Build" 10 | ], 11 | "author": "Aloïs Deniel", 12 | "version": { 13 | "Major": 0, 14 | "Minor": 2, 15 | "Patch": 33 16 | }, 17 | "groups": [], 18 | "instanceNameFormat": "Flutter Command", 19 | "inputs": [ 20 | { 21 | "name": "arguments", 22 | "type": "string", 23 | "label": "Arguments", 24 | "required": false, 25 | "defaultValue": "", 26 | "helpMarkDown": "Arguments passed to the flutter command." 27 | }, 28 | { 29 | "name": "projectDirectory", 30 | "type": "filePath", 31 | "label": "Path to the project folder", 32 | "defaultValue": ".", 33 | "helpMarkDown": "The path to the project folder (which contains the `pubspec.yaml`)", 34 | "required": true 35 | } 36 | ], 37 | "execution": { 38 | "Node": { 39 | "target": "index.js" 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /tasks/command/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs" 5 | }, 6 | "exclude": [ 7 | "node_modules" 8 | ] 9 | } -------------------------------------------------------------------------------- /tasks/command/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:latest" 4 | ], 5 | "rules": { 6 | "member-ordering": [ false ], 7 | "interface-name": [ false ], 8 | "object-literal-sort-keys": false, 9 | 10 | "whitespace": [ 11 | "check-branch", 12 | "check-decl", 13 | "check-operator", 14 | "check-module", 15 | "check-separator", 16 | "check-type" 17 | ], 18 | 19 | "one-line": [ 20 | "check-open-brace", 21 | "check-whitespace" 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /tasks/install/.taskkey: -------------------------------------------------------------------------------- 1 | 6b2b67e2-238f-433a-8236-04d2651a9445 -------------------------------------------------------------------------------- /tasks/install/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/tasks/install/icon.png -------------------------------------------------------------------------------- /tasks/install/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 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 | -------------------------------------------------------------------------------- /tasks/install/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const path = require("path"); 13 | const os = require("os"); 14 | const https = require("https"); 15 | const task = require("azure-pipelines-task-lib"); 16 | const tool = require("azure-pipelines-tool-lib/tool"); 17 | const FLUTTER_TOOL_NAME = 'Flutter'; 18 | const FLUTTER_EXE_RELATIVEPATH = 'flutter/bin'; 19 | const FLUTTER_TOOL_PATH_ENV_VAR = 'FlutterToolPath'; 20 | function main() { 21 | return __awaiter(this, void 0, void 0, function* () { 22 | // 1. Getting current platform identifier 23 | let arch = findArchitecture(); 24 | // 2. Building version spec 25 | let channel = task.getInput('channel', true); 26 | var version = task.getInput('version', true); 27 | if (version === 'custom') { 28 | version = task.getInput('customVersion', true); 29 | } 30 | let sdkInfo = yield findSdkInformation(channel, arch, version); 31 | // 3. Check if already available 32 | task.debug(`Trying to get (${FLUTTER_TOOL_NAME},${sdkInfo.version}, ${arch}) tool from local cache`); 33 | let toolPath = tool.findLocalTool(FLUTTER_TOOL_NAME, sdkInfo.version, arch); 34 | if (!toolPath) { 35 | // 4.1. Downloading SDK 36 | yield downloadAndCacheSdk(sdkInfo, channel, arch); 37 | // 4.2. Verifying that tool is now available 38 | task.debug(`Trying again to get (${FLUTTER_TOOL_NAME},${sdkInfo.version}, ${arch}) tool from local cache`); 39 | toolPath = tool.findLocalTool(FLUTTER_TOOL_NAME, sdkInfo.version, arch); 40 | } 41 | if (toolPath) { 42 | // 5. Creating the environment variable 43 | let fullFlutterPath = path.join(toolPath, FLUTTER_EXE_RELATIVEPATH); 44 | task.debug(`Set ${FLUTTER_TOOL_PATH_ENV_VAR} with '${fullFlutterPath}'`); 45 | task.setVariable(FLUTTER_TOOL_PATH_ENV_VAR, fullFlutterPath); 46 | task.setResult(task.TaskResult.Succeeded, "Installed"); 47 | } 48 | else { 49 | task.setResult(task.TaskResult.Failed, "Download succedeeded but ToolPath not found."); 50 | } 51 | }); 52 | } 53 | /// Finds current running architecture : macos, linux or windows. 54 | function findArchitecture() { 55 | if (os.platform() === 'darwin') 56 | return "macos"; 57 | else if (os.platform() === 'linux') 58 | return "linux"; 59 | return "windows"; 60 | } 61 | function findSdkInformation(channel, arch, version) { 62 | return __awaiter(this, void 0, void 0, function* () { 63 | let json = yield getJSON('storage.googleapis.com', `/flutter_infra/releases/releases_${arch}.json`); 64 | var current = null; 65 | if (version === 'latest') { 66 | let currentHash = json.current_release[channel]; 67 | current = json.releases.find((item) => item.hash === currentHash); 68 | } 69 | else { 70 | current = json.releases.find((item) => uniformizeVersion(item.version) === uniformizeVersion(version)); 71 | } 72 | if (!current) { 73 | throw Error(`No version ${version} found in release history.`); 74 | } 75 | current.version = uniformizeVersion(current.version); 76 | return { 77 | version: current.version + '-' + channel, 78 | downloadUrl: json.base_url + '/' + current.archive, 79 | }; 80 | }); 81 | } 82 | function downloadAndCacheSdk(sdkInfo, channel, arch) { 83 | return __awaiter(this, void 0, void 0, function* () { 84 | // 1. Download SDK archive 85 | task.debug(`Starting download archive from '${sdkInfo.downloadUrl}'`); 86 | var bundleZip = yield tool.downloadTool(sdkInfo.downloadUrl); 87 | task.debug(`Succeeded to download '${bundleZip}' archive from '${sdkInfo.downloadUrl}'`); 88 | // 2. Extracting SDK bundle 89 | task.debug(`Extracting '${sdkInfo.downloadUrl}' archive`); 90 | var bundleDir = yield tool.extractZip(bundleZip); 91 | task.debug(`Extracted to '${bundleDir}' '${sdkInfo.downloadUrl}' archive`); 92 | // 3. Adding SDK bundle to cache 93 | task.debug(`Adding '${bundleDir}' to cache (${FLUTTER_TOOL_NAME},${sdkInfo.version}, ${arch})`); 94 | tool.cacheDir(bundleDir, FLUTTER_TOOL_NAME, sdkInfo.version, arch); 95 | }); 96 | } 97 | main().catch(error => { 98 | task.setResult(task.TaskResult.Failed, error); 99 | }); 100 | /// Removes the 'v' prefix from given version. 101 | function uniformizeVersion(version) { 102 | if (version.startsWith('v')) { 103 | return version.substring(1); 104 | } 105 | return version; 106 | } 107 | /// Sends an https request and parses the result as JSON. 108 | function getJSON(hostname, path) { 109 | return __awaiter(this, void 0, void 0, function* () { 110 | return new Promise((resolve, reject) => { 111 | let options = { 112 | hostname: hostname, 113 | port: 443, 114 | path: path, 115 | method: 'GET', 116 | }; 117 | const req = https.request(options, res => { 118 | let data = ''; 119 | // A chunk of data has been recieved. 120 | res.on('data', (chunk) => { 121 | data += chunk; 122 | }); 123 | // The whole response has been received. Print out the result. 124 | res.on('end', () => { 125 | try { 126 | resolve(JSON.parse(data)); 127 | } 128 | catch (e) { 129 | reject(e); 130 | } 131 | }); 132 | }); 133 | req.on('error', error => { 134 | reject(error); 135 | }); 136 | req.end(); 137 | }); 138 | }); 139 | } 140 | -------------------------------------------------------------------------------- /tasks/install/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as os from 'os'; 3 | import * as https from 'https'; 4 | import * as task from "azure-pipelines-task-lib"; 5 | import * as tool from 'azure-pipelines-tool-lib/tool'; 6 | 7 | const FLUTTER_TOOL_NAME: string = 'Flutter'; 8 | const FLUTTER_EXE_RELATIVEPATH = 'flutter/bin'; 9 | const FLUTTER_TOOL_PATH_ENV_VAR: string = 'FlutterToolPath'; 10 | 11 | async function main(): Promise { 12 | // 1. Getting current platform identifier 13 | let arch = findArchitecture(); 14 | 15 | // 2. Building version spec 16 | let channel = task.getInput('channel', true); 17 | var version = task.getInput('version', true); 18 | if (version === 'custom') { 19 | version = task.getInput('customVersion', true); 20 | } 21 | 22 | let sdkInfo = await findSdkInformation(channel, arch, version); 23 | 24 | // 3. Check if already available 25 | task.debug(`Trying to get (${FLUTTER_TOOL_NAME},${sdkInfo.version}, ${arch}) tool from local cache`); 26 | let toolPath = tool.findLocalTool(FLUTTER_TOOL_NAME, sdkInfo.version, arch); 27 | 28 | if (!toolPath) { 29 | // 4.1. Downloading SDK 30 | await downloadAndCacheSdk(sdkInfo, channel, arch); 31 | 32 | // 4.2. Verifying that tool is now available 33 | task.debug(`Trying again to get (${FLUTTER_TOOL_NAME},${sdkInfo.version}, ${arch}) tool from local cache`); 34 | toolPath = tool.findLocalTool(FLUTTER_TOOL_NAME, sdkInfo.version, arch); 35 | } 36 | 37 | if (toolPath) { 38 | // 5. Creating the environment variable 39 | let fullFlutterPath: string = path.join(toolPath, FLUTTER_EXE_RELATIVEPATH); 40 | task.debug(`Set ${FLUTTER_TOOL_PATH_ENV_VAR} with '${fullFlutterPath}'`); 41 | task.setVariable(FLUTTER_TOOL_PATH_ENV_VAR, fullFlutterPath); 42 | task.setResult(task.TaskResult.Succeeded, "Installed"); 43 | } 44 | else { 45 | task.setResult(task.TaskResult.Failed, "Download succedeeded but ToolPath not found."); 46 | } 47 | } 48 | 49 | /// Finds current running architecture : macos, linux or windows. 50 | function findArchitecture() { 51 | if (os.platform() === 'darwin') 52 | return "macos"; 53 | else if (os.platform() === 'linux') 54 | return "linux"; 55 | return "windows"; 56 | } 57 | 58 | async function findSdkInformation(channel: string, arch: string, version: string): Promise<{ downloadUrl: string, version: string }> { 59 | let json = await getJSON('storage.googleapis.com', `/flutter_infra/releases/releases_${arch}.json`); 60 | var current = null; 61 | 62 | if (version === 'latest') { 63 | let currentHash = json.current_release[channel]; 64 | current = json.releases.find((item: { hash: any; }) => item.hash === currentHash); 65 | } 66 | else { 67 | current = json.releases.find((item: { version: any; }) => uniformizeVersion(item.version) === uniformizeVersion(version)); 68 | } 69 | 70 | if (!current) { 71 | throw Error(`No version ${version} found in release history.`); 72 | } 73 | 74 | current.version = uniformizeVersion(current.version); 75 | 76 | return { 77 | version: current.version + '-' + channel, 78 | downloadUrl: json.base_url + '/' + current.archive, 79 | }; 80 | } 81 | 82 | async function downloadAndCacheSdk(sdkInfo: { downloadUrl: string, version: string }, channel: string, arch: string): Promise { 83 | // 1. Download SDK archive 84 | task.debug(`Starting download archive from '${sdkInfo.downloadUrl}'`); 85 | var bundleZip = await tool.downloadTool(sdkInfo.downloadUrl); 86 | task.debug(`Succeeded to download '${bundleZip}' archive from '${sdkInfo.downloadUrl}'`); 87 | 88 | // 2. Extracting SDK bundle 89 | task.debug(`Extracting '${sdkInfo.downloadUrl}' archive`); 90 | var bundleDir = await tool.extractZip(bundleZip); 91 | task.debug(`Extracted to '${bundleDir}' '${sdkInfo.downloadUrl}' archive`); 92 | 93 | // 3. Adding SDK bundle to cache 94 | task.debug(`Adding '${bundleDir}' to cache (${FLUTTER_TOOL_NAME},${sdkInfo.version}, ${arch})`); 95 | tool.cacheDir(bundleDir, FLUTTER_TOOL_NAME, sdkInfo.version, arch); 96 | } 97 | 98 | main().catch(error => { 99 | task.setResult(task.TaskResult.Failed, error); 100 | }); 101 | 102 | /// Removes the 'v' prefix from given version. 103 | function uniformizeVersion(version: string): string { 104 | if (version.startsWith('v')) { 105 | return version.substring(1); 106 | } 107 | return version; 108 | } 109 | 110 | /// Sends an https request and parses the result as JSON. 111 | async function getJSON(hostname: string, path: string): Promise { 112 | return new Promise((resolve, reject) => { 113 | let options: https.RequestOptions = { 114 | hostname: hostname, 115 | port: 443, 116 | path: path, 117 | method: 'GET', 118 | }; 119 | 120 | const req = https.request(options, res => { 121 | let data = ''; 122 | 123 | // A chunk of data has been recieved. 124 | res.on('data', (chunk) => { 125 | data += chunk; 126 | }); 127 | 128 | // The whole response has been received. Print out the result. 129 | res.on('end', () => { 130 | try { 131 | resolve(JSON.parse(data)) 132 | } 133 | catch (e) { 134 | reject(e); 135 | } 136 | }); 137 | }) 138 | 139 | req.on('error', error => { 140 | reject(error); 141 | }) 142 | 143 | req.end() 144 | }); 145 | } -------------------------------------------------------------------------------- /tasks/install/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutterinstall", 3 | "version": "0.2.35", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "13.13.5", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.5.tgz", 10 | "integrity": "sha512-3ySmiBYJPqgjiHA7oEaIo2Rzz0HrOZ7yrNO5HWyaE5q0lQ3BppDZ3N53Miz8bw2I7gh1/zir2MGVZBvpb1zq9g==" 11 | }, 12 | "@types/semver": { 13 | "version": "5.5.0", 14 | "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", 15 | "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==" 16 | }, 17 | "@types/uuid": { 18 | "version": "3.4.4", 19 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz", 20 | "integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==", 21 | "requires": { 22 | "@types/node": "*" 23 | } 24 | }, 25 | "azure-pipelines-task-lib": { 26 | "version": "2.9.3", 27 | "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-2.9.3.tgz", 28 | "integrity": "sha512-SPWKSfgmjyBDVIMzXnnPH0Gv7YXZ+AQ3SyIhNNALAmQpOltqJhgslvzrOClR5rKuoOyJlG0AWZILbZIXCkztAA==", 29 | "requires": { 30 | "minimatch": "3.0.4", 31 | "mockery": "^1.7.0", 32 | "q": "^1.1.2", 33 | "semver": "^5.1.0", 34 | "shelljs": "^0.3.0", 35 | "uuid": "^3.0.1" 36 | } 37 | }, 38 | "azure-pipelines-tool-lib": { 39 | "version": "0.12.0", 40 | "resolved": "https://registry.npmjs.org/azure-pipelines-tool-lib/-/azure-pipelines-tool-lib-0.12.0.tgz", 41 | "integrity": "sha512-JAlFvMTtEXISrnJY/kgq0LecLi089RqXRf/gMsXYbflmzszklkc+LUJpR0A7NDmJ+9/MWpKY/ZX+Q/zirYa7gw==", 42 | "requires": { 43 | "@types/semver": "^5.3.0", 44 | "@types/uuid": "^3.0.1", 45 | "azure-pipelines-task-lib": "^2.8.0", 46 | "semver": "^5.3.0", 47 | "semver-compare": "^1.0.0", 48 | "typed-rest-client": "1.0.9", 49 | "uuid": "^3.0.1" 50 | }, 51 | "dependencies": { 52 | "typed-rest-client": { 53 | "version": "1.0.9", 54 | "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.0.9.tgz", 55 | "integrity": "sha512-iOdwgmnP/tF6Qs+oY4iEtCf/3fnCDl7Gy9LGPJ4E3M4Wj3uaSko15FVwbsaBmnBqTJORnXBWVY5306D4HH8oiA==", 56 | "requires": { 57 | "tunnel": "0.0.4", 58 | "underscore": "1.8.3" 59 | } 60 | } 61 | } 62 | }, 63 | "balanced-match": { 64 | "version": "1.0.0", 65 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 66 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 67 | }, 68 | "brace-expansion": { 69 | "version": "1.1.11", 70 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 71 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 72 | "requires": { 73 | "balanced-match": "^1.0.0", 74 | "concat-map": "0.0.1" 75 | } 76 | }, 77 | "concat-map": { 78 | "version": "0.0.1", 79 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 80 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 81 | }, 82 | "minimatch": { 83 | "version": "3.0.4", 84 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 85 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 86 | "requires": { 87 | "brace-expansion": "^1.1.7" 88 | } 89 | }, 90 | "mockery": { 91 | "version": "1.7.0", 92 | "resolved": "https://registry.npmjs.org/mockery/-/mockery-1.7.0.tgz", 93 | "integrity": "sha1-9O3g2HUMHJcnwnLqLGBiniyaHE8=" 94 | }, 95 | "q": { 96 | "version": "1.5.1", 97 | "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", 98 | "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" 99 | }, 100 | "semver": { 101 | "version": "5.5.1", 102 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", 103 | "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" 104 | }, 105 | "semver-compare": { 106 | "version": "1.0.0", 107 | "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", 108 | "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" 109 | }, 110 | "shelljs": { 111 | "version": "0.3.0", 112 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", 113 | "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" 114 | }, 115 | "tunnel": { 116 | "version": "0.0.4", 117 | "resolved": "http://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz", 118 | "integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=" 119 | }, 120 | "underscore": { 121 | "version": "1.8.3", 122 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", 123 | "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" 124 | }, 125 | "uuid": { 126 | "version": "3.3.2", 127 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 128 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /tasks/install/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutterinstall", 3 | "version": "0.2.35", 4 | "description": "Flutter Install Build Task", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "tsc" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/aloisdeniel/vsts-flutter-tasks.git" 12 | }, 13 | "author": "Aloïs Deniel", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/aloisdeniel/vsts-flutter-tasks/issues" 17 | }, 18 | "homepage": "https://github.com/aloisdeniel/vsts-flutter-tasks#readme", 19 | "dependencies": { 20 | "azure-pipelines-tool-lib": "0.12.0" 21 | }, 22 | "devDependencies": { 23 | "@types/node": "^13.13.5" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tasks/install/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "fca8d27d-85f4-4d5d-a519-8d7a7edf15d7", 3 | "name": "FlutterInstall", 4 | "friendlyName": "Flutter Install", 5 | "description": "Install the Flutter environment.", 6 | "helpMarkdown": "For more information, take a look at the Flutter [documentation](https://flutter.io)", 7 | "category": "Tool", 8 | "author": "Aloïs Deniel", 9 | "version": { 10 | "Major": 0, 11 | "Minor": 2, 12 | "Patch": 35 13 | }, 14 | "instanceNameFormat": "Flutter Install", 15 | "inputs": [ 16 | { 17 | "name": "channel", 18 | "type": "pickList", 19 | "label": "Channel", 20 | "defaultValue": "stable", 21 | "required": true, 22 | "helpMarkDown": "The build release channels (https://github.com/flutter/flutter/wiki/Flutter-build-release-channels).", 23 | "options": { 24 | "stable": "Stable", 25 | "beta": "Beta", 26 | "dev": "Dev" 27 | } 28 | }, 29 | { 30 | "name": "version", 31 | "type": "pickList", 32 | "label": "Version", 33 | "defaultValue": "latest", 34 | "required": true, 35 | "helpMarkDown": "Use the latest version of the Flutter SDK or a custom one.", 36 | "options": { 37 | "latest": "Latest", 38 | "custom": "Custom version" 39 | } 40 | }, 41 | { 42 | "name": "customVersion", 43 | "type": "string", 44 | "label": "Custom version (semver)", 45 | "defaultValue": "", 46 | "required": true, 47 | "helpMarkDown": "The version of the SDK to use (in a ..

semver format, from (https://flutter.io/sdk-archive/).", 48 | "visibleRule": "version = custom" 49 | } 50 | ], 51 | "execution": { 52 | "Node": { 53 | "target": "index.js" 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /tasks/install/tests/latest.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | const mr = require("azure-pipelines-task-lib/mock-run"); 6 | const taskPath = path.join(__dirname, "../index.js"); 7 | var runner = new mr.TaskMockRunner(taskPath); 8 | function assertDirectory(path) { 9 | if (!fs.existsSync(path)) { 10 | fs.mkdirSync(path); 11 | } 12 | } 13 | // ENV 14 | const tempPath = path.join(__dirname, "..", "..", "..", "temp"); 15 | const agentPath = path.join(tempPath, "agent"); 16 | process.env["BUILD_BUILDNUMBER"] = "1"; 17 | assertDirectory(tempPath); 18 | assertDirectory(agentPath); 19 | assertDirectory(process.env["AGENT_HOMEDIRECTORY"] = path.join(agentPath, "home")); 20 | assertDirectory(process.env["AGENT_TOOLSDIRECTORY"] = path.join(agentPath, "tools")); 21 | assertDirectory(process.env["AGENT_TEMPDIRECTORY"] = path.join(agentPath, "temp")); 22 | assertDirectory(process.env["AGENT_BUILDDIRECTORY"] = path.join(agentPath, "build")); 23 | //let tmr = require('vsts-task-lib/mock-toolrunner'); 24 | //runner.registerMock('vsts-task-lib/toolrunner', tmr); 25 | runner.setInput("channel", "beta"); 26 | runner.setInput("version", "latest"); 27 | runner.run(true); 28 | -------------------------------------------------------------------------------- /tasks/install/tests/latest.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as fs from 'fs'; 3 | import * as mr from 'azure-pipelines-task-lib/mock-run'; 4 | 5 | const taskPath = path.join(__dirname, "../index.js"); 6 | var runner = new mr.TaskMockRunner(taskPath); 7 | 8 | function assertDirectory(path: string) { 9 | if (!fs.existsSync(path)) { 10 | fs.mkdirSync(path); 11 | } 12 | } 13 | 14 | // ENV 15 | const tempPath = path.join(__dirname, "..", "..", "..", "temp"); 16 | const agentPath = path.join(tempPath, "agent"); 17 | process.env["BUILD_BUILDNUMBER"] = "1"; 18 | assertDirectory(tempPath); 19 | assertDirectory(agentPath); 20 | assertDirectory(process.env["AGENT_HOMEDIRECTORY"] = path.join(agentPath, "home")); 21 | assertDirectory(process.env["AGENT_TOOLSDIRECTORY"] = path.join(agentPath, "tools")); 22 | assertDirectory(process.env["AGENT_TEMPDIRECTORY"] = path.join(agentPath, "temp")); 23 | assertDirectory(process.env["AGENT_BUILDDIRECTORY"] = path.join(agentPath, "build")); 24 | 25 | //let tmr = require('vsts-task-lib/mock-toolrunner'); 26 | //runner.registerMock('vsts-task-lib/toolrunner', tmr); 27 | 28 | runner.setInput("channel", "beta"); 29 | runner.setInput("version", "latest"); 30 | 31 | runner.run(true); -------------------------------------------------------------------------------- /tasks/install/tests/notexisting.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | const mr = require("azure-pipelines-task-lib/mock-run"); 6 | const taskPath = path.join(__dirname, "../index.js"); 7 | var runner = new mr.TaskMockRunner(taskPath); 8 | function assertDirectory(path) { 9 | if (!fs.existsSync(path)) { 10 | fs.mkdirSync(path); 11 | } 12 | } 13 | // ENV 14 | const tempPath = path.join(__dirname, "..", "..", "..", "temp"); 15 | const agentPath = path.join(tempPath, "agent"); 16 | process.env["BUILD_BUILDNUMBER"] = "1"; 17 | assertDirectory(tempPath); 18 | assertDirectory(agentPath); 19 | assertDirectory(process.env["AGENT_HOMEDIRECTORY"] = path.join(agentPath, "home")); 20 | assertDirectory(process.env["AGENT_TOOLSDIRECTORY"] = path.join(agentPath, "tools")); 21 | assertDirectory(process.env["AGENT_TEMPDIRECTORY"] = path.join(agentPath, "temp")); 22 | assertDirectory(process.env["AGENT_BUILDDIRECTORY"] = path.join(agentPath, "build")); 23 | //let tmr = require('vsts-task-lib/mock-toolrunner'); 24 | //runner.registerMock('vsts-task-lib/toolrunner', tmr); 25 | runner.setInput("channel", "beta"); 26 | runner.setInput("version", "not-existing"); 27 | runner.run(true); 28 | -------------------------------------------------------------------------------- /tasks/install/tests/notexisting.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as fs from 'fs'; 3 | import * as mr from 'azure-pipelines-task-lib/mock-run'; 4 | 5 | const taskPath = path.join(__dirname, "../index.js"); 6 | var runner = new mr.TaskMockRunner(taskPath); 7 | 8 | function assertDirectory(path: string) { 9 | if (!fs.existsSync(path)) { 10 | fs.mkdirSync(path); 11 | } 12 | } 13 | 14 | // ENV 15 | const tempPath = path.join(__dirname, "..", "..", "..", "temp"); 16 | const agentPath = path.join(tempPath, "agent"); 17 | process.env["BUILD_BUILDNUMBER"] = "1"; 18 | assertDirectory(tempPath); 19 | assertDirectory(agentPath); 20 | assertDirectory(process.env["AGENT_HOMEDIRECTORY"] = path.join(agentPath, "home")); 21 | assertDirectory(process.env["AGENT_TOOLSDIRECTORY"] = path.join(agentPath, "tools")); 22 | assertDirectory(process.env["AGENT_TEMPDIRECTORY"] = path.join(agentPath, "temp")); 23 | assertDirectory(process.env["AGENT_BUILDDIRECTORY"] = path.join(agentPath, "build")); 24 | 25 | //let tmr = require('vsts-task-lib/mock-toolrunner'); 26 | //runner.registerMock('vsts-task-lib/toolrunner', tmr); 27 | 28 | runner.setInput("channel", "beta"); 29 | runner.setInput("version", "not-existing"); 30 | 31 | runner.run(true); -------------------------------------------------------------------------------- /tasks/install/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs" 5 | }, 6 | "exclude": [ 7 | "node_modules" 8 | ] 9 | } -------------------------------------------------------------------------------- /tasks/install/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:latest" 4 | ], 5 | "rules": { 6 | "member-ordering": [ false ], 7 | "interface-name": [ false ], 8 | "object-literal-sort-keys": false, 9 | 10 | "whitespace": [ 11 | "check-branch", 12 | "check-decl", 13 | "check-operator", 14 | "check-module", 15 | "check-separator", 16 | "check-type" 17 | ], 18 | 19 | "one-line": [ 20 | "check-open-brace", 21 | "check-whitespace" 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /tasks/test/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aloisdeniel/vsts-flutter-tasks/6be8dc268a9c596c27ee6496f91cfe1b8561ef12/tasks/test/icon.png -------------------------------------------------------------------------------- /tasks/test/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 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 | -------------------------------------------------------------------------------- /tasks/test/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { 3 | function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 4 | return new (P || (P = Promise))(function (resolve, reject) { 5 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } 6 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } 7 | function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } 8 | step((generator = generator.apply(thisArg, _arguments || [])).next()); 9 | }); 10 | }; 11 | Object.defineProperty(exports, "__esModule", { value: true }); 12 | const path = require("path"); 13 | const xml2js = require("xml2js"); 14 | const task = require("azure-pipelines-task-lib/task"); 15 | const FLUTTER_TOOL_PATH_ENV_VAR = 'FlutterToolPath'; 16 | function main() { 17 | return __awaiter(this, void 0, void 0, function* () { 18 | // 1. Check flutter environment 19 | var flutterPath = task.getVariable(FLUTTER_TOOL_PATH_ENV_VAR) || process.env[FLUTTER_TOOL_PATH_ENV_VAR]; 20 | flutterPath = path.join(flutterPath, "flutter"); 21 | if (!flutterPath) { 22 | throw new Error(`The '${FLUTTER_TOOL_PATH_ENV_VAR}' environment variable must be set before using this task (you can use 'flutterinstall' task).`); 23 | } 24 | // 3. Move current working directory to project 25 | let projectDirectory = task.getPathInput('projectDirectory', false, false); 26 | if (projectDirectory) { 27 | task.debug(`Moving to ${projectDirectory}`); 28 | task.cd(projectDirectory); 29 | } 30 | // 4. Get inputs 31 | let testName = task.getInput('testName', false); 32 | let testPlainName = task.getInput('testPlainName', false); 33 | let updateGoldens = task.getBoolInput('updateGoldens', false); 34 | let concurrency = task.getInput('concurrency', false); 35 | // 5. Running tests 36 | var results = yield runTests(flutterPath, (concurrency ? Number(concurrency) : null), updateGoldens, testName, testPlainName); 37 | // 6. Publishing tests 38 | yield publishTests(results); 39 | if (results.isSuccess) { 40 | task.setResult(task.TaskResult.Succeeded, `All tests passed`); 41 | } 42 | else { 43 | task.setResult(task.TaskResult.Failed, `Some tests failed`); 44 | } 45 | }); 46 | } 47 | function publishTests(results) { 48 | return __awaiter(this, void 0, void 0, function* () { 49 | var publisher = new task.TestPublisher("JUnit"); 50 | task.debug(`results: ` + JSON.stringify(results)); 51 | // 1. Generating Junit XML result file 52 | var junitResults = createJunitResults(results); 53 | var xmlBuilder = new xml2js.Builder(); 54 | var xml = xmlBuilder.buildObject(junitResults); 55 | var xmlPath = path.join(task.cwd(), "junit.xml"); 56 | task.writeFile(xmlPath, xml); 57 | // 2. Publishing to task 58 | publisher.publish(xmlPath, 'false', "", "", "", 'true', "Flutter"); 59 | }); 60 | } 61 | function runTests(flutter, concurrency, updateGoldens, name, plainName) { 62 | return __awaiter(this, void 0, void 0, function* () { 63 | let testRunner = task.tool(flutter); 64 | testRunner.arg(['test', '--pub']); 65 | if (updateGoldens) { 66 | testRunner.arg("--update-goldens"); 67 | } 68 | if (name) { 69 | testRunner.arg("--name=" + name); 70 | } 71 | if (plainName) { 72 | testRunner.arg("--plain-name=" + plainName); 73 | } 74 | if (concurrency) { 75 | testRunner.arg("--concurrency=" + concurrency); 76 | } 77 | var currentSuite = null; 78 | var results = { 79 | isSuccess: false, 80 | suites: [] 81 | }; 82 | testRunner.on('stdout', line => { 83 | const testSuiteRegex = /\s*\d\d:\d\d (\+\d+)?(\s+\-\d+)?:\s*loading\s*(.*\.dart)\s*/; 84 | let loadingMatch = testSuiteRegex.exec(line); 85 | if (loadingMatch) { 86 | var newSuite = { 87 | title: path.basename(loadingMatch[3], ".dart"), 88 | isSuccess: false, 89 | succeeded: 0, 90 | failed: 0, 91 | cases: [] 92 | }; 93 | if (!currentSuite || newSuite.title !== currentSuite.title) { 94 | currentSuite = newSuite; 95 | results.suites.push(newSuite); 96 | } 97 | } 98 | else { 99 | createTestCase(currentSuite, line); 100 | } 101 | }); 102 | try { 103 | yield testRunner.exec(); 104 | results.isSuccess = true; 105 | } 106 | catch (_a) { } 107 | return results; 108 | }); 109 | } 110 | function createTestCase(suite, output) { 111 | const testRunRegex = /\s*\d\d:\d\d (\+\d+)?(\s+\-\d+)?:\s*(.*)/; 112 | let match = testRunRegex.exec(output); 113 | if (match) { 114 | var title = match[3]; 115 | var successes = Number(match[1]); 116 | var failures = match[2] ? -Number(match[2]) : suite.failed; 117 | var newCase = { 118 | title: title.trim(), 119 | isSuccess: false, 120 | started: new Date(), 121 | ended: new Date, 122 | }; 123 | var hasNewCase = false; 124 | if (suite.succeeded != successes) { 125 | suite.succeeded = successes; 126 | newCase.isSuccess = true; 127 | hasNewCase = true; 128 | } 129 | else if (suite.failed != failures) { 130 | suite.failed = failures; 131 | newCase.isSuccess = false; 132 | hasNewCase = true; 133 | } 134 | if (hasNewCase) { 135 | if (suite.cases.length > 0) { 136 | suite.cases[suite.cases.length - 1].ended = newCase.started; 137 | } 138 | suite.cases.push(newCase); 139 | } 140 | } 141 | } 142 | function createJunitResults(results) { 143 | var testSuites = []; 144 | results.suites.forEach(suite => { 145 | var testCases = []; 146 | suite.cases.forEach(c => { 147 | var duration = (c.ended.getTime() - c.started.getTime()); 148 | var s = (duration / 1000); 149 | var testCase = { 150 | "$": { 151 | "name": c.title, 152 | "classname": c.title, 153 | "time": s, 154 | } 155 | }; 156 | if (!c.isSuccess) { 157 | testCase["failure"] = { 158 | "$": { 159 | "type": "FlutterError", 160 | } 161 | }; 162 | } 163 | testCases.push(testCase); 164 | }); 165 | var testSuite = { 166 | "$": { 167 | "name": suite.title, 168 | "timestamp": new Date().toISOString(), 169 | "errors": 0, 170 | "skipped": 0, 171 | "failures": suite.failed, 172 | "tests": (suite.failed + suite.succeeded) 173 | }, 174 | "testcase": testCases 175 | }; 176 | testSuites.push(testSuite); 177 | }); 178 | return { 179 | "testsuites": { 180 | "testsuite": testSuites 181 | } 182 | }; 183 | } 184 | main().catch(error => { 185 | task.setResult(task.TaskResult.Failed, error); 186 | }); 187 | -------------------------------------------------------------------------------- /tasks/test/index.ts: -------------------------------------------------------------------------------- 1 | import * as path from "path"; 2 | import * as xml2js from "xml2js"; 3 | import * as task from "azure-pipelines-task-lib/task"; 4 | 5 | const FLUTTER_TOOL_PATH_ENV_VAR: string = 'FlutterToolPath'; 6 | 7 | async function main(): Promise { 8 | // 1. Check flutter environment 9 | var flutterPath = task.getVariable(FLUTTER_TOOL_PATH_ENV_VAR) || process.env[FLUTTER_TOOL_PATH_ENV_VAR]; 10 | flutterPath = path.join(flutterPath, "flutter") 11 | if (!flutterPath) { 12 | throw new Error(`The '${FLUTTER_TOOL_PATH_ENV_VAR}' environment variable must be set before using this task (you can use 'flutterinstall' task).`); 13 | } 14 | 15 | // 3. Move current working directory to project 16 | let projectDirectory = task.getPathInput('projectDirectory', false, false); 17 | if (projectDirectory) { 18 | task.debug(`Moving to ${projectDirectory}`); 19 | task.cd(projectDirectory); 20 | } 21 | 22 | // 4. Get inputs 23 | let testName = task.getInput('testName', false); 24 | let testPlainName = task.getInput('testPlainName', false); 25 | let updateGoldens = task.getBoolInput('updateGoldens', false); 26 | let concurrency = task.getInput('concurrency', false); 27 | 28 | // 5. Running tests 29 | var results = await runTests(flutterPath, (concurrency ? Number(concurrency) : null), updateGoldens, testName, testPlainName); 30 | 31 | // 6. Publishing tests 32 | await publishTests(results); 33 | 34 | if (results.isSuccess) { 35 | task.setResult(task.TaskResult.Succeeded, `All tests passed`); 36 | } 37 | else { 38 | task.setResult(task.TaskResult.Failed, `Some tests failed`); 39 | } 40 | } 41 | 42 | async function publishTests(results: any) { 43 | var publisher = new task.TestPublisher("JUnit"); 44 | 45 | task.debug(`results: ` + JSON.stringify(results)); 46 | 47 | // 1. Generating Junit XML result file 48 | var junitResults = createJunitResults(results); 49 | var xmlBuilder = new xml2js.Builder(); 50 | var xml = xmlBuilder.buildObject(junitResults); 51 | var xmlPath = path.join(task.cwd(), "junit.xml"); 52 | task.writeFile(xmlPath, xml); 53 | 54 | // 2. Publishing to task 55 | publisher.publish(xmlPath, 'false', "", "", "", 'true', "Flutter"); 56 | } 57 | 58 | async function runTests(flutter: string, concurrency?: number, updateGoldens?: boolean, name?: string, plainName?: string) { 59 | let testRunner = task.tool(flutter); 60 | testRunner.arg(['test', '--pub']); 61 | 62 | if (updateGoldens) { 63 | testRunner.arg("--update-goldens"); 64 | } 65 | 66 | if (name) { 67 | testRunner.arg("--name=" + name); 68 | } 69 | 70 | if (plainName) { 71 | testRunner.arg("--plain-name=" + plainName); 72 | } 73 | 74 | if (concurrency) { 75 | testRunner.arg("--concurrency=" + concurrency); 76 | } 77 | 78 | var currentSuite: any = null; 79 | var results = { 80 | isSuccess: false, 81 | suites: [] 82 | }; 83 | 84 | testRunner.on('stdout', line => { 85 | const testSuiteRegex = /\s*\d\d:\d\d (\+\d+)?(\s+\-\d+)?:\s*loading\s*(.*\.dart)\s*/; 86 | let loadingMatch = testSuiteRegex.exec(line); 87 | if (loadingMatch) { 88 | var newSuite = { 89 | title: path.basename(loadingMatch[3], ".dart"), 90 | isSuccess: false, 91 | succeeded: 0, 92 | failed: 0, 93 | cases: [] 94 | } 95 | 96 | if (!currentSuite || newSuite.title !== currentSuite.title) { 97 | currentSuite = newSuite; 98 | results.suites.push(newSuite); 99 | } 100 | } 101 | else { 102 | createTestCase(currentSuite, line); 103 | } 104 | }); 105 | 106 | try { 107 | await testRunner.exec(); 108 | results.isSuccess = true; 109 | } 110 | catch { } 111 | 112 | return results; 113 | } 114 | 115 | function createTestCase(suite: any, output: string) { 116 | const testRunRegex = /\s*\d\d:\d\d (\+\d+)?(\s+\-\d+)?:\s*(.*)/; 117 | let match = testRunRegex.exec(output); 118 | if (match) { 119 | var title = match[3]; 120 | var successes = Number(match[1]); 121 | var failures = match[2] ? -Number(match[2]) : suite.failed; 122 | 123 | var newCase = { 124 | title: title.trim(), 125 | isSuccess: false, 126 | started: new Date(), 127 | ended: new Date, 128 | }; 129 | 130 | var hasNewCase = false; 131 | 132 | if (suite.succeeded != successes) { 133 | suite.succeeded = successes; 134 | newCase.isSuccess = true; 135 | hasNewCase = true; 136 | } 137 | else if (suite.failed != failures) { 138 | suite.failed = failures; 139 | newCase.isSuccess = false; 140 | hasNewCase = true; 141 | } 142 | 143 | if (hasNewCase) { 144 | if (suite.cases.length > 0) { 145 | suite.cases[suite.cases.length - 1].ended = newCase.started; 146 | } 147 | suite.cases.push(newCase); 148 | } 149 | } 150 | } 151 | 152 | function createJunitResults(results: any) { 153 | var testSuites = []; 154 | 155 | results.suites.forEach(suite => { 156 | var testCases = []; 157 | suite.cases.forEach(c => { 158 | var duration = (c.ended.getTime() - c.started.getTime()); 159 | var s = (duration / 1000); 160 | var testCase = { 161 | "$": { 162 | "name": c.title, 163 | "classname": c.title, 164 | "time": s, 165 | } 166 | }; 167 | 168 | if (!c.isSuccess) { 169 | testCase["failure"] = { 170 | "$": { 171 | "type": "FlutterError", 172 | } 173 | } 174 | } 175 | 176 | testCases.push(testCase); 177 | }); 178 | 179 | var testSuite = { 180 | "$": { 181 | "name": suite.title, 182 | "timestamp": new Date().toISOString(), 183 | "errors": 0, // TODO 184 | "skipped": 0, // TODO 185 | "failures": suite.failed, 186 | "tests": (suite.failed + suite.succeeded) 187 | }, 188 | "testcase": testCases 189 | }; 190 | testSuites.push(testSuite); 191 | }); 192 | 193 | return { 194 | "testsuites": { 195 | "testsuite": testSuites 196 | } 197 | }; 198 | } 199 | 200 | main().catch(error => { 201 | task.setResult(task.TaskResult.Failed, error); 202 | }); -------------------------------------------------------------------------------- /tasks/test/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fluttertest", 3 | "version": "1.2.32", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/node": { 8 | "version": "13.13.5", 9 | "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.5.tgz", 10 | "integrity": "sha512-3ySmiBYJPqgjiHA7oEaIo2Rzz0HrOZ7yrNO5HWyaE5q0lQ3BppDZ3N53Miz8bw2I7gh1/zir2MGVZBvpb1zq9g==" 11 | }, 12 | "@types/semver": { 13 | "version": "5.5.0", 14 | "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", 15 | "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==" 16 | }, 17 | "@types/uuid": { 18 | "version": "3.4.4", 19 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.4.tgz", 20 | "integrity": "sha512-tPIgT0GUmdJQNSHxp0X2jnpQfBSTfGxUMc/2CXBU2mnyTFVYVa2ojpoQ74w0U2yn2vw3jnC640+77lkFFpdVDw==", 21 | "requires": { 22 | "@types/node": "*" 23 | } 24 | }, 25 | "@types/xml2js": { 26 | "version": "0.4.5", 27 | "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.5.tgz", 28 | "integrity": "sha512-yohU3zMn0fkhlape1nxXG2bLEGZRc1FeqF80RoHaYXJN7uibaauXfhzhOJr1Xh36sn+/tx21QAOf07b/xYVk1w==", 29 | "requires": { 30 | "@types/node": "*" 31 | } 32 | }, 33 | "azure-pipelines-task-lib": { 34 | "version": "2.9.3", 35 | "resolved": "https://registry.npmjs.org/azure-pipelines-task-lib/-/azure-pipelines-task-lib-2.9.3.tgz", 36 | "integrity": "sha512-SPWKSfgmjyBDVIMzXnnPH0Gv7YXZ+AQ3SyIhNNALAmQpOltqJhgslvzrOClR5rKuoOyJlG0AWZILbZIXCkztAA==", 37 | "requires": { 38 | "minimatch": "3.0.4", 39 | "mockery": "^1.7.0", 40 | "q": "^1.1.2", 41 | "semver": "^5.1.0", 42 | "shelljs": "^0.3.0", 43 | "uuid": "^3.0.1" 44 | } 45 | }, 46 | "azure-pipelines-tool-lib": { 47 | "version": "0.12.0", 48 | "resolved": "https://registry.npmjs.org/azure-pipelines-tool-lib/-/azure-pipelines-tool-lib-0.12.0.tgz", 49 | "integrity": "sha512-JAlFvMTtEXISrnJY/kgq0LecLi089RqXRf/gMsXYbflmzszklkc+LUJpR0A7NDmJ+9/MWpKY/ZX+Q/zirYa7gw==", 50 | "requires": { 51 | "@types/semver": "^5.3.0", 52 | "@types/uuid": "^3.0.1", 53 | "azure-pipelines-task-lib": "^2.8.0", 54 | "semver": "^5.3.0", 55 | "semver-compare": "^1.0.0", 56 | "typed-rest-client": "1.0.9", 57 | "uuid": "^3.0.1" 58 | }, 59 | "dependencies": { 60 | "typed-rest-client": { 61 | "version": "1.0.9", 62 | "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.0.9.tgz", 63 | "integrity": "sha512-iOdwgmnP/tF6Qs+oY4iEtCf/3fnCDl7Gy9LGPJ4E3M4Wj3uaSko15FVwbsaBmnBqTJORnXBWVY5306D4HH8oiA==", 64 | "requires": { 65 | "tunnel": "0.0.4", 66 | "underscore": "1.8.3" 67 | } 68 | } 69 | } 70 | }, 71 | "balanced-match": { 72 | "version": "1.0.0", 73 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 74 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" 75 | }, 76 | "brace-expansion": { 77 | "version": "1.1.11", 78 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 79 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 80 | "requires": { 81 | "balanced-match": "^1.0.0", 82 | "concat-map": "0.0.1" 83 | } 84 | }, 85 | "concat-map": { 86 | "version": "0.0.1", 87 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 88 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" 89 | }, 90 | "minimatch": { 91 | "version": "3.0.4", 92 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 93 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 94 | "requires": { 95 | "brace-expansion": "^1.1.7" 96 | } 97 | }, 98 | "mockery": { 99 | "version": "1.7.0", 100 | "resolved": "https://registry.npmjs.org/mockery/-/mockery-1.7.0.tgz", 101 | "integrity": "sha1-9O3g2HUMHJcnwnLqLGBiniyaHE8=" 102 | }, 103 | "q": { 104 | "version": "1.5.1", 105 | "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", 106 | "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" 107 | }, 108 | "sax": { 109 | "version": "1.2.4", 110 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 111 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" 112 | }, 113 | "semver": { 114 | "version": "5.5.1", 115 | "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.1.tgz", 116 | "integrity": "sha512-PqpAxfrEhlSUWge8dwIp4tZnQ25DIOthpiaHNIthsjEFQD6EvqUKUDM7L8O2rShkFccYo1VjJR0coWfNkCubRw==" 117 | }, 118 | "semver-compare": { 119 | "version": "1.0.0", 120 | "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", 121 | "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=" 122 | }, 123 | "shelljs": { 124 | "version": "0.3.0", 125 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", 126 | "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" 127 | }, 128 | "tunnel": { 129 | "version": "0.0.4", 130 | "resolved": "http://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz", 131 | "integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=" 132 | }, 133 | "underscore": { 134 | "version": "1.8.3", 135 | "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", 136 | "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" 137 | }, 138 | "uuid": { 139 | "version": "3.3.2", 140 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", 141 | "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" 142 | }, 143 | "xml2js": { 144 | "version": "0.4.23", 145 | "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", 146 | "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", 147 | "requires": { 148 | "sax": ">=0.6.0", 149 | "xmlbuilder": "~11.0.0" 150 | } 151 | }, 152 | "xmlbuilder": { 153 | "version": "11.0.1", 154 | "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", 155 | "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /tasks/test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fluttertest", 3 | "version": "1.2.32", 4 | "description": "Flutter Test Task", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/aloisdeniel/vsts-flutter-tasks.git" 12 | }, 13 | "author": "Aloïs Deniel", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/aloisdeniel/vsts-flutter-tasks/issues" 17 | }, 18 | "homepage": "https://github.com/aloisdeniel/vsts-flutter-tasks#readme", 19 | "dependencies": { 20 | "azure-pipelines-tool-lib": "0.12.0", 21 | "@types/xml2js": "0.4.5", 22 | "xml2js": "0.4.23" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tasks/test/task.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "6bee7ef4-7576-42da-b464-c654d0ef0cb9", 3 | "name": "FlutterTest", 4 | "friendlyName": "Flutter Test", 5 | "description": "Executes all tests for a Flutter project.", 6 | "helpMarkdown": "Don't forget to insert a 'Flutter Install' task before. For more information, take a look at the Flutter [documentation](https://flutter.io)", 7 | "category": "Test", 8 | "author": "Aloïs Deniel", 9 | "version": { 10 | "Major": 0, 11 | "Minor": 2, 12 | "Patch": 32 13 | }, 14 | "instanceNameFormat": "Flutter Test", 15 | "inputs": [ 16 | { 17 | "name": "projectDirectory", 18 | "type": "filePath", 19 | "label": "Path to the project folder", 20 | "defaultValue": "", 21 | "helpMarkDown": "The path to the project folder (which contains the `pubspec.yaml`)", 22 | "required": true 23 | }, 24 | { 25 | "name": "testName", 26 | "type": "string", 27 | "label": "Test name", 28 | "defaultValue": "", 29 | "required": false, 30 | "helpMarkDown": "A regular expression matching substrings of the names of tests to run." 31 | }, 32 | { 33 | "name": "testPlainName", 34 | "type": "string", 35 | "label": "Test plain name", 36 | "defaultValue": "", 37 | "required": false, 38 | "helpMarkDown": "A plain-text substring of the names of tests to run." 39 | }, 40 | { 41 | "name": "updateGoldens", 42 | "type": "boolean", 43 | "label": "Update goldens", 44 | "defaultValue": false, 45 | "required": false, 46 | "helpMarkDown": "Whether matchesGoldenFile() calls within your test methods should update the golden files rather than test for an existing match." 47 | }, 48 | { 49 | "name": "concurrency", 50 | "type": "string", 51 | "label": "Concurrency", 52 | "defaultValue": "", 53 | "required": false, 54 | "helpMarkDown": "The number of concurrent test processes to run. (defaults to '6')" 55 | } 56 | ], 57 | "execution": { 58 | "Node": { 59 | "target": "index.js" 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /tasks/test/tests/basic.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | const path = require("path"); 4 | const fs = require("fs"); 5 | const mr = require("azure-pipelines-task-lib/mock-run"); 6 | const taskPath = path.join(__dirname, "../index.js"); 7 | var runner = new mr.TaskMockRunner(taskPath); 8 | function assertDirectory(path) { 9 | if (!fs.existsSync(path)) { 10 | fs.mkdirSync(path); 11 | } 12 | } 13 | // ENV 14 | const rootPath = path.join(__dirname, "..", "..", ".."); 15 | const tempPath = path.join(rootPath, "temp"); 16 | const agentPath = path.join(tempPath, "agent"); 17 | const dropPath = path.join(tempPath, "drop"); 18 | process.env["BUILD_BUILDNUMBER"] = "1"; 19 | assertDirectory(tempPath); 20 | assertDirectory(agentPath); 21 | assertDirectory(dropPath); 22 | assertDirectory(process.env["AGENT_HOMEDIRECTORY"] = path.join(agentPath, "home")); 23 | assertDirectory(process.env["AGENT_TOOLSDIRECTORY"] = path.join(agentPath, "tools")); 24 | assertDirectory(process.env["AGENT_TEMPDIRECTORY"] = path.join(agentPath, "temp")); 25 | assertDirectory(process.env["AGENT_BUILDDIRECTORY"] = path.join(agentPath, "build")); 26 | // Run install tests 27 | process.env["FlutterToolPath"] = path.join(agentPath, "tools", "Flutter", "0.8.2-beta", "macos", "flutter", "bin"); 28 | runner.setInput("projectDirectory", path.join(rootPath, "sample_project")); 29 | runner.run(true); 30 | -------------------------------------------------------------------------------- /tasks/test/tests/basic.ts: -------------------------------------------------------------------------------- 1 | import * as path from 'path'; 2 | import * as fs from 'fs'; 3 | import * as mr from 'azure-pipelines-task-lib/mock-run'; 4 | 5 | const taskPath = path.join(__dirname, "../index.js"); 6 | var runner = new mr.TaskMockRunner(taskPath); 7 | 8 | function assertDirectory(path: string) { 9 | if (!fs.existsSync(path)) { 10 | fs.mkdirSync(path); 11 | } 12 | } 13 | 14 | // ENV 15 | const rootPath = path.join(__dirname, "..", "..", ".."); 16 | const tempPath = path.join(rootPath, "temp"); 17 | const agentPath = path.join(tempPath, "agent"); 18 | const dropPath = path.join(tempPath, "drop"); 19 | process.env["BUILD_BUILDNUMBER"] = "1"; 20 | assertDirectory(tempPath); 21 | assertDirectory(agentPath); 22 | assertDirectory(dropPath); 23 | assertDirectory(process.env["AGENT_HOMEDIRECTORY"] = path.join(agentPath, "home")); 24 | assertDirectory(process.env["AGENT_TOOLSDIRECTORY"] = path.join(agentPath, "tools")); 25 | assertDirectory(process.env["AGENT_TEMPDIRECTORY"] = path.join(agentPath, "temp")); 26 | assertDirectory(process.env["AGENT_BUILDDIRECTORY"] = path.join(agentPath, "build")); 27 | 28 | // Run install tests 29 | process.env["FlutterToolPath"] = path.join(agentPath, "tools", "Flutter", "0.8.2-beta", "macos", "flutter", "bin"); 30 | 31 | runner.setInput("projectDirectory", path.join(rootPath, "sample_project")); 32 | 33 | runner.run(true); -------------------------------------------------------------------------------- /tasks/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "module": "commonjs" 5 | }, 6 | "exclude": [ 7 | "node_modules" 8 | ] 9 | } -------------------------------------------------------------------------------- /tasks/test/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint:latest" 4 | ], 5 | "rules": { 6 | "member-ordering": [ false ], 7 | "interface-name": [ false ], 8 | "object-literal-sort-keys": false, 9 | 10 | "whitespace": [ 11 | "check-branch", 12 | "check-decl", 13 | "check-operator", 14 | "check-module", 15 | "check-separator", 16 | "check-type" 17 | ], 18 | 19 | "one-line": [ 20 | "check-open-brace", 21 | "check-whitespace" 22 | ] 23 | } 24 | } -------------------------------------------------------------------------------- /vss-extension.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": 1, 3 | "id": "flutter", 4 | "version": "0.2.39", 5 | "name": "Flutter", 6 | "description": "Flutter extension for Azure DevOps", 7 | "publisher": "aloisdeniel", 8 | "public": true, 9 | "targets": [ 10 | { 11 | "id": "Microsoft.VisualStudio.Services" 12 | } 13 | ], 14 | "categories": [ 15 | "Azure Pipelines" 16 | ], 17 | "icons": { 18 | "default": "icon.png" 19 | }, 20 | "screenshots": [], 21 | "content": { 22 | "details": { 23 | "path": "README.md" 24 | }, 25 | "license": { 26 | "path": "LICENSE" 27 | } 28 | }, 29 | "links": { 30 | "support": { 31 | "uri": "https://github.com/aloisdeniel/vsts-flutter-tasks/issues" 32 | } 33 | }, 34 | "repository": { 35 | "type": "git", 36 | "uri": "https://github.com/aloisdeniel/vsts-flutter-tasks" 37 | }, 38 | "tags": [ 39 | "flutter", 40 | "mobile", 41 | "ios", 42 | "android" 43 | ], 44 | "files": [ 45 | { 46 | "path": "tasks/install" 47 | }, 48 | { 49 | "path": "tasks/build" 50 | }, 51 | { 52 | "path": "tasks/test" 53 | }, 54 | { 55 | "path": "tasks/command" 56 | }, 57 | { 58 | "path": "images", 59 | "addressable": true 60 | } 61 | ], 62 | "contributions": [ 63 | { 64 | "id": "flutter-install", 65 | "type": "ms.vss-distributed-task.task", 66 | "targets": [ 67 | "ms.vss-distributed-task.tasks" 68 | ], 69 | "properties": { 70 | "name": "tasks/install" 71 | } 72 | }, 73 | { 74 | "id": "flutter-build", 75 | "type": "ms.vss-distributed-task.task", 76 | "targets": [ 77 | "ms.vss-distributed-task.tasks" 78 | ], 79 | "properties": { 80 | "name": "tasks/build" 81 | } 82 | }, 83 | { 84 | "id": "flutter-test", 85 | "type": "ms.vss-distributed-task.task", 86 | "targets": [ 87 | "ms.vss-distributed-task.tasks" 88 | ], 89 | "properties": { 90 | "name": "tasks/test" 91 | } 92 | }, 93 | { 94 | "id": "flutter-command", 95 | "type": "ms.vss-distributed-task.task", 96 | "targets": [ 97 | "ms.vss-distributed-task.tasks" 98 | ], 99 | "properties": { 100 | "name": "tasks/command" 101 | } 102 | } 103 | ] 104 | } --------------------------------------------------------------------------------