Skip to content

Commit

Permalink
Merge pull request #9 from OutSystems/feature/AddPipelines
Browse files Browse the repository at this point in the history
Add Pipelines
  • Loading branch information
GFOutsystems authored Apr 26, 2021
2 parents 85554f2 + 6e1d3e4 commit 652dd80
Show file tree
Hide file tree
Showing 24 changed files with 1,085 additions and 0 deletions.
75 changes: 75 additions & 0 deletions CI/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
### Development CI

The simplest of pipelines, will run and check the Unit tests in the plugin and in the end it will provide the PR the status of the tests.

Trigger: PR creation, PR Update

### Release CI

It will change extensibility of the plugin OML to this tag, update all the consumables, deploy from DEV to TST environment, build the apps, upload to sauce labs and trigger the UI tests pipelines and wait for their response.

Trigger: Tag creation

## Identified Problems

There is no way to know the MABS version that the build was build on right now. So when passing the version name to Sauce Labs the value will always be `MABS 7.0 Pipeline`. This will be changed in the future so the pipeline OML returns to us the verison.

### Configurations

There are some configurations that need to be taken care before running the release pipeline. This configurations will help the pipeline on tasks like finding the respective module, and consumables to build.

The configurations are:

## Module name

This will identify the main module to update the extensibility. It will change the extensibility to the correct branch/tag and update all the consumables of the module.

It can only have a single value.
Its automatically generated by the plugin template.

## Application Names

This will identify the application/modules that will be deployed from DEV to TST environment. This is important because the pipeline will check wich of this modules have setup mobile apps and run builds on them.

It can have multiple values, and multiple modules with apps.
Its automatically generated but more can be added or removed.

## Test Pipeline Args

# Plugin

The value that identifies the plugin in the UI Tests Pipeline. This variable needs to be checked/created in the UI Test Pipeline and added here. When the UI Tests pipeline runs it will run with the tests for this plugin.

It can only have one value.
Its not automatically generated, needs to be checked or created on UI Tests Pipelines variables.

# Threads

A value that identify the number of threads to run the UI Tests, this should be 2 or 3 usually.
And they might differ by plugin.

It can only have one string value representing a decimal number.
Its not automatically generated but as default it will be the value 3.

## Sauce Labs Info

This json object will have values for each sample app (modules with mobile apps configured). The sample app name must be also in the applicationNames configuration, and will have the keys needed for sauce labs.

# Android API Key - iOS API Key

This keys can be found on Sauce Labs.

Android
Legacy RDC -> Sample App Android platform -> top left corner settings wheels -> Appium -> Setup Instructions -> API Key

iOS
Legacy RDC -> Sample App iOS platform -> top left corner settings wheels -> Appium -> Setup Instructions -> API Key

Copy the key and insert inside the sample app object a sample app object should look like this:

```
"TestSampleApp": {
"androidAPIKey": "0X0ASDAKEY",
"iosAPIKey": "1X0ASDAKEY"
}
```
14 changes: 14 additions & 0 deletions CI/azure-pipeline-develop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: 'Develop'

trigger: none
pr:
autoCancel: true
branches:
include:
- master
- outsystems

stages:
- template: 'templates/tests-stage.yml'
parameters:
platforms: ['all']
46 changes: 46 additions & 0 deletions CI/azure-pipeline-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Squeleton of a could be a staging pipeline, stages need to be checked for the needs
# When a tag is set in the outsystems branch, a release pipeline will start that will:
# - run unit tests
# - deploy to test env and start UI Tests
# - deploy to outsystems release env

# Known problems UItests is a different pipeline.
variables:
- group: outsystemsPluginsCIAuthentication
- group: pipelineSupportOapURL
- group: sauceLabsUserVariables
- name: basicAuth
value: $[variables.basicAuthentication]
- name: pipelineURL
value: $[variables.productionEnvironmentURL]
- name: sauceLabsUser
value: $[variables.USER]
- name: destinationFolder
value: $(System.DefaultWorkingDirectory)/builds/

name: 'Release'

pr: none
trigger:
tags:
include:
- '*'

stages:
- template: 'templates/tests-stage.yml'
parameters:
platforms: ['all'] # Platforms to test for, it can be 'all' to test all ('ios', 'js', 'android')
- template: 'templates/oml-update-stage.yml'
parameters:
environment: 'enmobile11-dev.outsystemsenterprise.com'
basicAuthentication: $(basicAuth)
- template: 'templates/deployment-build-stage.yml'
parameters:
fromEnvironment: 'Development'
toEnvironment: 'Testing'
basicAuthentication: $(basicAuth)
pipelineURL: $(pipelineURL)
destinationFolder: $(destinationFolder)
runID: $(Build.BuildID)
sauceLabsUser: $(sauceLabsUser)
- template: 'templates/pipeline-run-stage.yml'
33 changes: 33 additions & 0 deletions CI/azure-pipeline-staging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Squeleton of a could be a staging pipeline, stages need to be checked for the needs
variables:
- group: outsystemsPluginsCIAuthentication
- group: pipelineSupportOapURL
- name: basicAuth
value: $[variables.basicAuthentication]
- name: pipelineURL
value: $[variables.productionEnvironmentURL]

name: 'Staging'

pr: none
trigger:
- master
- outsystems

stages:
- template: 'templates/tests-stage.yml'
parameters:
platforms: ['all']
- template: 'templates/oml-update-stage.yml'
parameters:
environment: 'enmobile11-dev.outsystemsenterprise.com'
basicAuthentication: $(basicAuth)
- template: 'templates/deployment-build-stage.yml'
parameters:
fromEnvironment: 'Development'
toEnvironment: 'Testing'
basicAuthentication: $(basicAuth)
pipelineURL: $(pipelineURL)
- template: 'templates/artifact-apps-stage.yml'
parameters:
runID: $(BuildID)
22 changes: 22 additions & 0 deletions CI/templates/android-emulator-step.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
steps:
- bash: |
#!/usr/bin/env bash
# Install AVD files
echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'system-images;android-27;google_apis;x86'
# Create emulator
echo "no" | $ANDROID_HOME/tools/bin/avdmanager create avd -n xamarin_android_emulator -k 'system-images;android-27;google_apis;x86' --force
$ANDROID_HOME/emulator/emulator -list-avds
echo "Starting emulator"
# Start emulator in background
nohup $ANDROID_HOME/emulator/emulator -avd xamarin_android_emulator -no-snapshot > /dev/null 2>&1 &
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do sleep 1; done; input keyevent 82'
$ANDROID_HOME/platform-tools/adb devices
echo "Emulator started"
name: StartEmulator
30 changes: 30 additions & 0 deletions CI/templates/android-tests-job.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
parameters:
- name: workingDirectory
type: string
default: ''
- name: dependsOn
type: string
default:
- name: condition
type: string
default:

jobs:
- job: AndroidTest
dependsOn: ${{ parameters.dependsOn }}
condition: ${{ parameters.condition }}
displayName: 'Android Tests'
pool:
vmImage: 'macOS'
steps:
- template: 'npm-steps.yml'
parameters:
workingDirectory: ${{ parameters.workingDirectory }}
- template: 'android-emulator-step.yml'
- task: Gradle@2
inputs:
cwd: '${{ parameters.workingDirectory }}/CalendarTests'
gradleWrapperFile: '${{ parameters.workingDirectory }}/CalendarTests/gradlew'
publishJUnitResults: true
testResultsFiles: '**/TEST-*.xml'
tasks: 'clean test cAT'
9 changes: 9 additions & 0 deletions CI/templates/artifact-apps-step.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# For now just a simple squeleton needs proper setting for deployment to ui test env
parameters:
- name: runID
type: string
default: ''

steps:
- script: 'npm run artifact --buildID=${{ parameters.runID }} --buildsPath="$(System.DefaultWorkingDirectory)/builds/"'
workingDirectory: 'CI/templates'
39 changes: 39 additions & 0 deletions CI/templates/artifactStage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const { exec } = require("child_process");
var fs = require("fs");

if(process.env.npm_config_buildID == null) {
throw new Error("Missing BuildID/RunID argument");
}

if(process.env.npm_config_buildsPath == null) {
throw new Error("Missing builds folder path argument \"buildsPath\"");
}

var dir = process.env.npm_config_buildsPath;
var runID = process.env.npm_config_buildID;

fs.readdirSync(dir).forEach(function(appDir) {
fs.stat(dir + appDir, (err, stats) => {
if (err) {
throw new Error(err);
} else if (stats.isDirectory()) {
fs.readdirSync(dir + appDir).forEach(function(appFile) {
archiveBuild(dir + appDir + "/" + appFile, appDir, appFile);
});
}
});
});

function archiveBuild(buildPath, appName, filename) {
exec("az pipelines runs artifact upload --artifact-name '" + appName + "." + filename + "' --path '" + buildPath + "' --run-id '" + runID +"'", (error, stdout, stderr) => {
if (error) {
console.log(`error: ${error.message}`);
return;
}
if (stderr) {
console.log(`stderr: ${stderr}`);
return;
}
console.log(`stdout: ${stdout}`);
});
}
65 changes: 65 additions & 0 deletions CI/templates/changeExtensibility.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
var configurations = require('./configurations.json');

if(configurations.moduleName == null) {
throw new Error("Missing moduleName configuration in package.json");
}

if(process.env.npm_config_repositoryURL == null || process.env.npm_config_branch == null || process.env.npm_config_environment == null) {
throw new Error("Missing repositoryURL, branch, environment arguments");
}

if(process.env.npm_config_authentication == null) {
throw new Error("Missing authentication argument");
}

var extensibilityChangeJson = readJSONFile("extensibilityConfiguration.json");

if(extensibilityChangeJson == null) {
throw new Error("Missing extensibilityConfiguration.json file");
}

var XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest;

var repository = process.env.npm_config_repositoryURL;
var branch = process.env.npm_config_branch;
var environment = process.env.npm_config_environment;
var moduleName = configurations.moduleName ;
var basicAuthentication = process.env.npm_config_authentication;

var url = "https://" + environment + "/CodeUpdater/rest/Bulk/ExtensabilityUpdate";

extensibilityChangeJson.plugin.url = repository+"#"+branch;

var extensibilityChangeString = JSON.stringify(extensibilityChangeJson);
var buffer = new Buffer.from(extensibilityChangeString);
var base64 = buffer.toString('base64');

var body = [{
"ModuleName": moduleName,
"Content": base64
}];

console.log(
"Started changing extensibility in module " + moduleName +
".\n -- Extensibility will be configured to: " + repository+"#"+branch +
"\nin environment:" + environment

);

var request = new XMLHttpRequest();
request.open("POST", url, false);
request.setRequestHeader("Authorization", basicAuthentication);
request.setRequestHeader("Content-Type", "application/json");
request.send(JSON.stringify(body));

if(request.status == 200) {
console.log("Successfully updated OML");
} else {
throw new Error("Network Error:" + JSON.stringify(request));
}

function readJSONFile(file) {
var fs = require('fs');
var data = fs.readFileSync(file, 'utf8');
return JSON.parse(data);
}
17 changes: 17 additions & 0 deletions CI/templates/configurations.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"moduleName": "CalendarPlugin",
"applicationNames": [
"CalendarPlugin",
"CalendarSampleApp"
],
"testPipelineArgs": {
"plugin": "calendar",
"threads": "2"
},
"sauceLabsInfo": {
"CalendarSampleApp": {
"androidAPIKey": "B856C096BA7445979AB4BA559DDB08E5",
"iosAPIKey": "7819856977554EE5805CFB5CA170AE72"
}
}
}
21 changes: 21 additions & 0 deletions CI/templates/deploy-test-env-stage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# For now just a simple squeleton needs proper setting for deployment to ui test env
parameters:
shouldRunTests: false
release: []


stages:
- stage: Deployment
jobs:
- job: deploy_test_env
displayName: 'Deployment Test Env'
pool:
vmImage: 'ubuntu-latest'
steps:
- script: 'echo Mocked Response successful deployment' # Mocked response
- ${{ if eq(parameters.shouldRunTests, true) }}:
- script: 'echo WIP Mocked Response UITests all passed' # Mocked response
- ${{ if containsValue(parameters.release, 'github') }}:
- script: 'echo WIP Mocked Response successful release to github' # Mocked response
- ${{ if containsValue(parameters.release, 'outsystems') }}:
- script: 'echo WIP Mocked Response successful release to outsystems' # Mocked response
Loading

0 comments on commit 652dd80

Please sign in to comment.