diff options
| author | Menny Even Danan <menny@evendanan.net> | 2020-03-02 15:03:03 +0000 |
|---|---|---|
| committer | Menny Even Danan <menny@evendanan.net> | 2020-03-10 15:12:01 +0000 |
| commit | 75a0d3e3a6ba8bb187e2957c4f9e34c5b4f6ed5b (patch) | |
| tree | 4157a08b179fb4c1febc6d3d4c8b6cada633d3ea | |
| parent | 2e9762f38bb0f779d75990b0e18687d64b5137a1 (diff) | |
| download | AnySoftKeyboard-75a0d3e3a6ba8bb187e2957c4f9e34c5b4f6ed5b.tar.gz AnySoftKeyboard-75a0d3e3a6ba8bb187e2957c4f9e34c5b4f6ed5b.tar.bz2 | |
Deploy-Request Gradle task and definition of promoting steps
24 files changed, 536 insertions, 209 deletions
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index d6f99749e..8ae18906e 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,4 +1,4 @@ -* @AnySoftKeyboard/infra @AnySoftKeyboard/maintainers +* @AnySoftKeyboard/maintainers #only organization owner can change the license /LICENSE @menny #core infra structure team can approve github configuration diff --git a/.github/actions/deploy-request/action.yml b/.github/actions/deploy-request/action.yml deleted file mode 100644 index 2ac16e04f..000000000 --- a/.github/actions/deploy-request/action.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: "deployment-request" -author: "menny" -description: "Performs a deploy request" - -inputs: - ref: - description: "ref (branch) to publish" - required: true - sha: - description: "commit to deploy" - required: true - api_user_name: - description: "the username to use for API calls" - required: true - api_user_token: - description: "the user API token to use for API calls" - required: true - reports_folder: - description: "Where to store requests and responses files." - required: true - -runs: - using: "docker" - image: "docker://menny/ndk_ask:1.13.6" - entrypoint: /bin/bash - args: - - .github/actions/deploy-request/request.sh - - ${{ inputs.ref }} - - ${{ inputs.sha }} - - ${{ inputs.api_user_name }} - - ${{ inputs.api_user_token }} - - ${{ inputs.reports_folder }} - -branding: - icon: 'package' - color: 'purple' diff --git a/.github/actions/deploy-request/request.sh b/.github/actions/deploy-request/request.sh deleted file mode 100755 index d526f76f4..000000000 --- a/.github/actions/deploy-request/request.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash -set -e - -REF_TO_DEPLOY="${1}" -#we are using exact SHA to deploy, and not branc (which can move) -SHA_TO_DEPLOY="${2}" -API_USERNAME="${3}" -API_TOKEN="${4}" -OUTPUT="${5}" - -rm -rf "${OUTPUT}" || true -mkdir -p "${OUTPUT}" - -function deployment_request() { - echo "making request to: ${1}" - local JSON_TEXT - JSON_TEXT=$( jq -n \ - --arg jsonRef "${SHA_TO_DEPLOY}" \ - --arg jsonDeployTarget "${1}" \ - --arg jsonDescription "${2}" \ - '{ ref: $jsonRef, task: "deploy", auto_merge: false, environment: $jsonDeployTarget, description: $jsonDescription, required_contexts: [ "master-green-requirement" ] }' ) - - local JSON_FILENAME="${OUTPUT}/deployment_request_${1}.json" - echo "${JSON_TEXT}" > "${JSON_FILENAME}" - cat "${JSON_FILENAME}" - set +e - curl --fail -u "${API_USERNAME}:${API_TOKEN}" -o "${OUTPUT}/deployment_response_${1}.json" -d "@${JSON_FILENAME}" https://api.github.com/repos/AnySoftKeyboard/AnySoftKeyboard/deployments - local curl_exit_code=$? - set -e - echo "response with exit-code ${curl_exit_code}:" - cat "${OUTPUT}/deployment_response_${1}.json" - if [[ ${curl_exit_code} -ne 0 ]]; then - exit ${curl_exit_code} - fi -} - -#some deploy logic -if [[ "${REF_TO_DEPLOY}" == "refs/heads/master" ]]; then - deployment_request "app_alpha" "Deployment request by ${API_USERNAME}" - deployment_request "addons_alpha" "Deployment request by ${API_USERNAME}" -elif [[ "${REF_TO_DEPLOY}" == "release-branch-v"* ]]; then - deployment_request "app_beta" "Deployment request by ${API_USERNAME}" -fi diff --git a/.github/actions/deploy-status/action.yml b/.github/actions/deploy-status/action.yml deleted file mode 100644 index bb4592f0c..000000000 --- a/.github/actions/deploy-status/action.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: "deployment-status" -author: "menny" -description: "updates deployment status" - -inputs: - deployment_id: - description: "deployment-id" - required: true - state: - description: "commit to deploy" - required: true - environment: - description: "environment to deploy" - required: true - api_user_name: - description: "the username to use for API calls" - required: true - api_user_token: - description: "the user API token to use for API calls" - required: true - -runs: - using: "docker" - image: "docker://menny/ndk_ask:1.13.6" - entrypoint: /bin/bash - args: - - .github/actions/deploy-status/request.sh - - ${{ inputs.deployment_id }} - - ${{ inputs.environment }} - - ${{ inputs.state }} - - ${{ inputs.api_user_name }} - - ${{ inputs.api_user_token }} - -branding: - icon: 'package' - color: 'purple' diff --git a/.github/actions/deploy/action.yml b/.github/actions/deploy/action.yml index 8463f089a..f13035251 100644 --- a/.github/actions/deploy/action.yml +++ b/.github/actions/deploy/action.yml @@ -3,27 +3,45 @@ description: "Deploys the AnySoftKeyboard artifacts to Play Store" author: "menny" inputs: - deploy_target: - description: "Target to deploy. Should something deploy-request github-action outputs" + deployment_id: + description: "ID given by github" + required: true + deployment_environment: + description: "Deploy to which environment" + required: true + deployment_task: + description: "Deployment task" + required: true + api_user: + description: "github API user" + required: true + api_token: + description: "github API user token" required: true crash_report_email: default: "none@example.com" - required: false + description: "email address for crash reporting " + required: true keystore_url: default: "" - required: false + description: "direct download URL to APK signing keystore" + required: true keystore_password: default: "" - required: false + description: "APK signing keystore password" + required: true keystore_key_password: default: "" - required: false + description: "APK signing keystore default key password" + required: true publish_service_account_creds_url: + description: "direct download URL to Play-Store credentials file" default: "" - required: false + required: true publish_service_account: + description: "account for Play-Store API" default: "" - required: false + required: true runs: using: "docker" image: "docker://menny/ndk_ask:1.13.6" @@ -35,13 +53,17 @@ runs: entrypoint: /bin/bash args: - .github/actions/deploy/deploy.sh - - ${{ inputs.deploy_target }} + - ${{ inputs.deployment_id }} + - ${{ inputs.deployment_environment }} + - ${{ inputs.deployment_task }} - ${{ inputs.crash_report_email }} - ${{ inputs.keystore_url }} - ${{ inputs.keystore_password }} - ${{ inputs.keystore_key_password }} - ${{ inputs.publish_service_account_creds_url }} - ${{ inputs.publish_service_account }} + - ${{ inputs.api_user }} + - ${{ inputs.api_token }} branding: icon: 'upload-cloud' diff --git a/.github/actions/deploy/deploy.sh b/.github/actions/deploy/deploy.sh index 8bea7d299..f7d47a0d5 100755 --- a/.github/actions/deploy/deploy.sh +++ b/.github/actions/deploy/deploy.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash set -e - -DEPLOY_TARGET="${1}" +DEPLOYMET_ID="${1}" +shift +DEPLOYMENT_ENVIRONMENT="${1}" +shift +DEPLOYMENT_TASK="${1}" shift export ANYSOFTKEYBOARD_CRASH_REPORT_EMAIL="${1}" shift @@ -15,61 +18,101 @@ PUBLISH_CERT_FILE_URL="${1}" shift export PUBLISH_APK_SERVICE_ACCOUNT_EMAIL="${1}" shift +API_USER="${1}" +shift +API_TOKEN="${1}" +shift +function deployProcessFromEnvironmentName() { + #imeMaster_alpha_100 + [[ $1 =~ ([a-zA-Z]+)_.*_.* ]] + echo "${BASH_REMATCH[1]}" +} + +function deployChannelFromEnvironmentName() { + #imeMaster_alpha_100 + [[ $1 =~ .*_([a-zA-Z]+)_.* ]] + echo "${BASH_REMATCH[1]}" +} + +function deployFractionFromEnvironmentName() { + #imeMaster_alpha_100 + [[ $1 =~ .*_.*_([0-9]+) ]] + local PERCENTAGE="${BASH_REMATCH[1]}" + echo "$(echo "${PERCENTAGE}" | cut -c1-1).$(echo "${PERCENTAGE}" | cut -c2-3)" +} + +PROCESS_NAME=$(deployProcessFromEnvironmentName "${DEPLOYMENT_ENVIRONMENT}") +DEPLOY_CHANNEL=$(deployChannelFromEnvironmentName "${DEPLOYMENT_ENVIRONMENT}") +FRACTION=$(deployFractionFromEnvironmentName "${DEPLOYMENT_ENVIRONMENT}") + +echo "for ${DEPLOYMENT_ENVIRONMENT}: will deploy process ${PROCESS_NAME} to ${DEPLOY_CHANNEL} with ${FRACTION} fraction." export BUILD_COUNT_FOR_VERSION=${GITHUB_RUN_NUMBER} +./.github/actions/deploy/status-request.sh "${DEPLOYMET_ID}" "${DEPLOYMENT_ENVIRONMENT}" "in-progress" "${API_USER}" "${API_TOKEN}" + +echo "Downloading signature files..." if [[ -z "${KEYSTORE_FILE_URL}" ]]; then - echo "Using debug keystore for signing." - mkdir -p /root/.android/ || true - cp ./.github/actions/deploy/debug.keystore /root/.android/ || exit 1 + echo "Could not find secure env variable KEYSTORE_FILE_URL. Can not deploy." + exit 1 fi -DEPLOY_TASKS=( "-PwithAutoVersioning" ":generateFdroidYamls" ) -case "${DEPLOY_TARGET}" in - dry-run) - DEPLOY_TASKS+=( "-DdeployChannel=alpha" "assembleRelease" "assembleCanary" "verifyReleaseResources" "generateReleasePlayResources" "generateCanaryPlayResources" ) - ;; +if [[ -z "${PUBLISH_CERT_FILE_URL}" ]]; then + echo "Could not find secure env variable PUBLISH_CERT_FILE_URL. Can not deploy." + exit 1 +fi - app_alpha) - DEPLOY_TASKS+=( "-DdeployChannel=alpha" "ime:app:assembleCanary" "ime:app:publishCanary" ) - ;; +wget --tries=5 --waitretry=5 "${KEYSTORE_FILE_URL}" -q -O /tmp/anysoftkeyboard.keystore +stat /tmp/anysoftkeyboard.keystore +wget --tries=5 --waitretry=5 "${PUBLISH_CERT_FILE_URL}" -q -O /tmp/apk_upload_key.p12 +stat /tmp/apk_upload_key.p12 - app_beta) - DEPLOY_TASKS+=( "-DdeployChannel=beta" "ime:app:assembleRelease" "ime:app:publishRelease") - ;; +DEPLOY_TASKS=( "-PwithAutoVersioning" ":generateFdroidYamls" "-DdeployChannel=${DEPLOY_CHANNEL}" "--user-fraction" "${FRACTION}" ) +if [[ "${DEPLOYMENT_TASK}" == "deploy" ]]; then + case "${PROCESS_NAME}" in - addons_alpha) - DEPLOY_TASKS+=( "-DdeployChannel=alpha" "assembleRelease" "publishRelease" "-x" "ime:app:assembleRelease" "-x" "ime:app:publishRelease" ) - ;; + imeMaster) + DEPLOY_TASKS+=( "ime:app:assembleCanary" "ime:app:publishCanary" ) + ;; - *) - echo "deploy-target '${DEPLOY_TARGET}' is unkown!" - exit 1 - ;; -esac + imeProduction) + DEPLOY_TASKS+=( "ime:app:assembleRelease" "ime:app:publishRelease" ) + ;; + + addOns) + DEPLOY_TASKS+=( "assembleRelease" "publishRelease" "-x" "ime:app:assembleRelease" "-x" "ime:app:publishRelease" ) + ;; -echo "Counter is ${BUILD_COUNT_FOR_VERSION}, DEPLOY_TARGET: ${DEPLOY_TARGET}, crash email: ${ANYSOFTKEYBOARD_CRASH_REPORT_EMAIL}, and tasks: ${DEPLOY_TASKS[*]}" + *) + echo "PROCESS_NAME '${PROCESS_NAME}' is unknown in task ${DEPLOYMENT_TASK}!" + exit 1 + ;; -if [[ "${DEPLOY_TASKS[*]}" == *"publish"* ]]; then - echo "Downloading signature files..." + esac +elif [[ "${DEPLOYMENT_TASK}" == "deploy:migration" ]]; then + case "${PROCESS_NAME}" in - if [[ -z "${KEYSTORE_FILE_URL}" ]]; then - echo "Could not find secure env variable KEYSTORE_FILE_URL. Can not deploy." - exit 1 - fi + imeMaster) + DEPLOY_TASKS+=( "ime:app:promoteReleaseArtifact" ) + ;; - if [[ -z "${PUBLISH_CERT_FILE_URL}" ]]; then - echo "Could not find secure env variable PUBLISH_CERT_FILE_URL. Can not deploy." - exit 1 - fi + imeProduction) + DEPLOY_TASKS+=( "ime:app:promoteReleaseArtifact" ) + ;; - wget --tries=5 --waitretry=5 "${KEYSTORE_FILE_URL}" -q -O /tmp/anysoftkeyboard.keystore - stat /tmp/anysoftkeyboard.keystore - wget --tries=5 --waitretry=5 "${PUBLISH_CERT_FILE_URL}" -q -O /tmp/apk_upload_key.p12 - stat /tmp/apk_upload_key.p12 + addOns) + DEPLOY_TASKS+=( "promoteReleaseArtifact" "-x" "ime:app:promoteReleaseArtifact" ) + ;; + + esac fi -# shellcheck disable=SC2086 +echo "Counter is ${BUILD_COUNT_FOR_VERSION}, crash email: ${ANYSOFTKEYBOARD_CRASH_REPORT_EMAIL}, and tasks: ${DEPLOY_TASKS[*]}" + ./gradlew "${DEPLOY_TASKS[@]}" +./.github/actions/deploy/status-request.sh "${DEPLOYMET_ID}" "${DEPLOY_TARGET}" "success" "${API_USER}" "${API_TOKEN}" + +## TODO: kill previous enabled environments + [[ -n "${GITHUB_ACTIONS}" ]] && chmod -R a+rwx . diff --git a/.github/actions/deploy-status/request.sh b/.github/actions/deploy/status-request.sh index f2be576f9..f2be576f9 100755 --- a/.github/actions/deploy-status/request.sh +++ b/.github/actions/deploy/status-request.sh diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index f50c93807..f23635919 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -165,7 +165,6 @@ jobs: path: build-logging deploy-dry-run: - if: github.event_name == 'pull_request' runs-on: ubuntu-18.04 container: menny/ndk_ask:1.13.6 steps: @@ -178,9 +177,12 @@ jobs: dry-run-deploy-gradle- - name: setup run: ./scripts/ci/ci_setup.sh - - uses: ./.github/actions/deploy - with: - deploy_target: dry-run + - name: dry-run-release-build + run: | + mkdir -p /root/.android/ || true + cp ./.github/actions/deploy/debug.keystore /root/.android/ || exit 1 + ./gradlew -PwithAutoVersioning :generateFdroidYamls -DdeployChannel=alpha assembleRelease assembleCanary verifyReleaseResources generateReleasePlayResources generateCanaryPlayResources + chmod -R a+rwx . - uses: actions/upload-artifact@v1.0.0 with: name: fdroid-metadata-dry-run @@ -190,19 +192,17 @@ jobs: needs: [checks, app-all-sdks-tests, app-tests-shards, app-less-tests, deploy-dry-run] runs-on: ubuntu-18.04 steps: - - uses: actions/checkout@v2 + - name: ready + run: echo "DONE" deploy-request: if: github.event_name == 'push' needs: [master-green-requirement] runs-on: ubuntu-18.04 - container: menny/ndk_ask:1.13.6 + container: menny/android_base:1.13.6 steps: - uses: actions/checkout@v2 - - uses: ./.github/actions/deploy-request - with: - ref: ${{ github.ref }} - sha: ${{ github.sha }} - api_user_name: ${{ secrets.BOT_MASTER_RW_GITHUB_USERNAME }} - api_user_token: ${{ secrets.BOT_MASTER_RW_GITHUB_TOKEN }} - reports_folder: build/deploy-reports + - name: request-new-deploys + run: | + ./gradlew :deployment:imeOnMasterPush :deployment:addOnsOnMasterPush \ + -PrequestDeploy.sha=${{ github.sha }} -PrequestDeploy.api_user_name=${{ secrets.BOT_MASTER_RW_GITHUB_USERNAME }} -PrequestDeploy.api_user_token=${{ secrets.BOT_MASTER_RW_GITHUB_TOKEN }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index f44c86db2..c4f92dfdc 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -22,13 +22,6 @@ jobs: with: name: github_object path: build/github_object - - uses: ./.github/actions/deploy-status - with: - deployment_id: ${{ github.event.deployment.id }} - state: in_progress - environment: ${{ github.event.deployment.environment }} - api_user_name: ${{ secrets.BOT_MASTER_RW_GITHUB_USERNAME }} - api_user_token: ${{ secrets.BOT_MASTER_RW_GITHUB_TOKEN }} - name: setup run: | ./scripts/ci/ci_setup.sh @@ -37,29 +30,25 @@ jobs: mkdir -p ime/app/build/outputs/mapping || true - uses: ./.github/actions/deploy with: - deploy_target: ${{ github.event.deployment.environment }} + deployment_id: ${{ github.event.deployment.id }} + deployment_environment: ${{ github.event.deployment.environment }} + deployment_task: ${{ github.event.deployment.task }} crash_report_email: ${{ secrets.ANYSOFTKEYBOARD_CRASH_REPORT_EMAIL }} keystore_url: ${{ secrets.ANYSOFTKEYBOARD_KEYSTORE_URL }} keystore_password: ${{ secrets.ANYSOFTKEYBOARD_KEYSTORE_PASSWORD }} keystore_key_password: ${{ secrets.ANYSOFTKEYBOARD_KEYSTORE_KEY_PASSWORD }} publish_service_account_creds_url: ${{ secrets.PUBLISH_CERT_FILE_URL }} publish_service_account: ${{ secrets.PUBLISH_APK_SERVICE_ACCOUNT_EMAIL }} - - uses: ./.github/actions/deploy-status - if: success() - with: - deployment_id: ${{ github.event.deployment.id }} - state: success - environment: ${{ github.event.deployment.environment }} - api_user_name: ${{ secrets.BOT_MASTER_RW_GITHUB_USERNAME }} - api_user_token: ${{ secrets.BOT_MASTER_RW_GITHUB_TOKEN }} - - uses: ./.github/actions/deploy-status + api_user: ${{ secrets.BOT_MASTER_RW_GITHUB_USERNAME }} + api_token: ${{ secrets.BOT_MASTER_RW_GITHUB_TOKEN }} + - name: status-failure if: failure() + run: ./.github/actions/deploy/status-request.sh "${{ github.event.deployment.id }}" "${{ github.event.deployment.environment }}" "failure" "${{ secrets.BOT_MASTER_RW_GITHUB_USERNAME }}" "${{ secrets.BOT_MASTER_RW_GITHUB_TOKEN }}" + - uses: actions/upload-artifact@v1.0.0 + if: always() with: - deployment_id: ${{ github.event.deployment.id }} - state: failure - environment: ${{ github.event.deployment.environment }} - api_user_name: ${{ secrets.BOT_MASTER_RW_GITHUB_USERNAME }} - api_user_token: ${{ secrets.BOT_MASTER_RW_GITHUB_TOKEN }} + name: deploy-logging + path: build-logging - uses: actions/upload-artifact@v1.0.0 with: name: apks diff --git a/.github/workflows/deployment_promote.yml b/.github/workflows/deployment_promote.yml new file mode 100644 index 000000000..b7940f188 --- /dev/null +++ b/.github/workflows/deployment_promote.yml @@ -0,0 +1,21 @@ +name: deployment + +#always run on the default branch: master +on: + schedule: + - cron: '04 04 * * *' + +env: + TERM: dumb + GRADLE_OPTS: "-Dorg.gradle.daemon=false --stacktrace" + +jobs: + promote-alpha: + runs-on: ubuntu-18.04 + container: menny/android_base:1.13.6 + steps: + - uses: actions/checkout@v2 + - name: requets-pre-release-promote + run: | + ./gradlew :deployment:imePromoteMaster :deployment:addOnsPromoteMaster \ + -PrequestDeploy.sha=${{ github.sha }} -PrequestDeploy.api_user_name=${{ secrets.BOT_MASTER_RW_GITHUB_USERNAME }} -PrequestDeploy.api_user_token=${{ secrets.BOT_MASTER_RW_GITHUB_TOKEN }} @@ -32,10 +32,10 @@ Sign up to beta-channel [here](https://play.google.com/apps/testing/com.menny.an * More on AnySoftKeyboard can be found [here](http://anysoftkeyboard.github.io/). ## Releases -* Every commit to _master_ branch will deploy a new release to the _ALPHA_ channel in Google Play-Store. You can subscribe to this release channel by joining the [Google Groups](https://groups.google.com/d/forum/anysoftkeyboard-alpha-testers) group, and opt-in by visiting [this link](https://play.google.com/apps/testing/com.menny.android.anysoftkeyboard). -* Every once in a while, a stable enough _ALPHA_ will be promoted to _BETA_. You can opt-in to this channel by visiting [this link](https://play.google.com/apps/testing/com.menny.android.anysoftkeyboard). +* Every commit to _master_ branch will [deploy](.github/workflows/checks.yml) a new release to the _ALPHA_ channel in Google Play-Store. You can subscribe to this release channel by joining the [Google Groups](https://groups.google.com/d/forum/anysoftkeyboard-alpha-testers) group, and opt-in by visiting [this link](https://play.google.com/apps/testing/com.menny.android.anysoftkeyboard). +* Every Wednesday the latest _ALPHA_ will be [promoted](.github/workflows/deployment_promote.yml) to _BETA_. You can opt-in to this channel by visiting [this link](https://play.google.com/apps/testing/com.menny.android.anysoftkeyboard). * Note about pre-release channels: every few months we will remove all the users in the groups. When that happens, you are required to re-subscribe to the group. This is done to ensure that the members in the groups are active. -* Once all requirements for a release were finished, a _STABLE_ release branch (in the format of `release-branch-vX.X-rX`) will be cut. Every commit to this branch will be automatically published to Google Play Store, and will roll-out users gradually. +* Once all requirements for a release were finished, a _STABLE_ release branch (in the format of `release-branch-vX.X-rX`) will be cut. Every commit to this branch will be automatically published to Google Play Store (_STABLE_ channel) and will roll-out users gradually. ## Read more * Our fancy [web-site](http://anysoftkeyboard.github.io/) diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index f0ea90a8a..59579c2fb 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -4,19 +4,27 @@ apply plugin: 'java-gradle-plugin' repositories { maven { url 'https://repo1.maven.org/maven2' /*maven-central with HTTPS*/} + maven { url "https://plugins.gradle.org/m2/" } } dependencies { implementation 'org.jsoup:jsoup:1.9.1' implementation gradleApi() implementation localGroovy() + + implementation 'org.apache.httpcomponents:httpclient:4.5.11' + implementation 'com.google.code.gson:gson:2.8.6' } gradlePlugin { plugins { - simplePlugin { + makeDictionaryPlugin { id = 'make-dictionary' implementationClass = 'MakeDictionaryPlugin' } + deploymentsPlugin { + id = 'anysoftkeyboard-deployment' + implementationClass = 'deployment.DeploymentPlugin' + } } } diff --git a/buildSrc/src/main/java/deployment/DeploymentCommandLineArgs.java b/buildSrc/src/main/java/deployment/DeploymentCommandLineArgs.java new file mode 100644 index 000000000..26418e8d0 --- /dev/null +++ b/buildSrc/src/main/java/deployment/DeploymentCommandLineArgs.java @@ -0,0 +1,13 @@ +package deployment; + +class DeploymentCommandLineArgs { + final String sha; + final String apiUsername; + final String apiUserToken; + + DeploymentCommandLineArgs(String sha, String apiUsername, String apiUserToken) { + this.sha = sha; + this.apiUsername = apiUsername; + this.apiUserToken = apiUserToken; + } +} diff --git a/buildSrc/src/main/java/deployment/DeploymentPlugin.java b/buildSrc/src/main/java/deployment/DeploymentPlugin.java new file mode 100644 index 000000000..954d24eae --- /dev/null +++ b/buildSrc/src/main/java/deployment/DeploymentPlugin.java @@ -0,0 +1,47 @@ +package deployment; + +import java.util.ArrayList; +import java.util.Locale; +import org.gradle.api.NamedDomainObjectContainer; +import org.gradle.api.Plugin; +import org.gradle.api.Project; + +public class DeploymentPlugin implements Plugin<Project> { + + @Override + public void apply(Project project) { + final NamedDomainObjectContainer<DeploymentProcessConfiguration> configs = + project.container(DeploymentProcessConfiguration.class); + configs.all( + config -> { + config.environmentSteps = new ArrayList<>(); + }); + project.getExtensions().add("deployments", configs); + + project.afterEvaluate(this::createDeployTasks); + } + + private void createDeployTasks(Project project) { + final NamedDomainObjectContainer<DeploymentProcessConfiguration> configs = + (NamedDomainObjectContainer<DeploymentProcessConfiguration>) + project.getExtensions().findByName("deployments"); + configs.all( + config -> { + for (int stepIndex = 0; + stepIndex < config.environmentSteps.size(); + stepIndex++) { + final String stepName = config.environmentSteps.get(stepIndex); + project.getTasks() + .register( + String.format( + Locale.ROOT, + "deploymentRequest_%s_%s", + config.name, + stepName), + DeploymentRequestProcessTask.class, + config, + stepIndex); + } + }); + } +} diff --git a/buildSrc/src/main/java/deployment/DeploymentProcessConfiguration.java b/buildSrc/src/main/java/deployment/DeploymentProcessConfiguration.java new file mode 100644 index 000000000..4663d4551 --- /dev/null +++ b/buildSrc/src/main/java/deployment/DeploymentProcessConfiguration.java @@ -0,0 +1,14 @@ +package deployment; + +import java.util.ArrayList; +import java.util.List; + +public class DeploymentProcessConfiguration { + public final String name; + + public List<String> environmentSteps = new ArrayList<>(); + + public DeploymentProcessConfiguration(String name) { + this.name = name; + } +} diff --git a/buildSrc/src/main/java/deployment/DeploymentRequestProcessTask.java b/buildSrc/src/main/java/deployment/DeploymentRequestProcessTask.java new file mode 100644 index 000000000..a9da0040b --- /dev/null +++ b/buildSrc/src/main/java/deployment/DeploymentRequestProcessTask.java @@ -0,0 +1,105 @@ +package deployment; + +import github.Deployment; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.stream.Collectors; +import javax.inject.Inject; +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.TaskAction; + +public class DeploymentRequestProcessTask extends DefaultTask { + + private final DeploymentProcessConfiguration mConfiguration; + private final int mStepIndex; + + @Input + public String getEnvironmentKey() { + return getEnvironmentName(mConfiguration, mStepIndex); + } + + @Inject + public DeploymentRequestProcessTask( + DeploymentProcessConfiguration configuration, int stepIndex) { + mConfiguration = configuration; + mStepIndex = stepIndex; + setGroup("Publishing"); + setDescription("Request deployment of " + getEnvironmentName(configuration, stepIndex)); + } + + @TaskAction + public void deploymentRequestAction() { + try { + deploymentRequest(getProject().getProperties(), mConfiguration, mStepIndex); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void deploymentRequest( + Map<String, ?> properties, DeploymentProcessConfiguration configuration, int stepIndex) + throws Exception { + final DeploymentCommandLineArgs data = + new DeploymentCommandLineArgs( + properties.get("requestDeploy.sha").toString(), + properties.get("requestDeploy.api_user_name").toString(), + properties.get("requestDeploy.api_user_token").toString()); + + Deployment deployment = new Deployment(data.apiUsername, data.apiUserToken); + if (stepIndex == 0) { + requestNewDeploy(deployment, data, configuration); + } else { + throw new UnsupportedOperationException( + "step " + stepIndex + " for " + configuration.name + " is not implemented!"); + } + } + + private static void requestNewDeploy( + Deployment deployment, + DeploymentCommandLineArgs data, + DeploymentProcessConfiguration environment) + throws Exception { + final String environmentToDeploy = getEnvironmentName(environment, 0); + final List<String> environmentsToKill = + environment.environmentSteps.stream() + .map(name -> getEnvironmentName(environment.name, name)) + .filter(env -> !env.equals(environmentToDeploy)) + .collect(Collectors.toList()); + + final Deployment.Response response = + deployment.requestDeployment( + new Deployment.Request( + data.sha, + "deploy", + false, + environmentToDeploy, + String.format( + Locale.ROOT, + "Deployment for '%s' request by '%s'.", + environmentToDeploy, + data.apiUsername), + Collections.singletonList("master-green-requirement"), + new Deployment.RequestPayloadField(environmentsToKill))); + + System.out.println( + String.format( + Locale.ROOT, + "Deploy request response: id %s, sha %s, environment %s, task %s.", + response.id, + response.sha, + response.environment, + response.task)); + } + + private static String getEnvironmentName(String environmentName, String stepName) { + return String.format(Locale.ROOT, "%s_%s", environmentName, stepName); + } + + private static String getEnvironmentName( + DeploymentProcessConfiguration environment, int index) { + return getEnvironmentName(environment.name, environment.environmentSteps.get(index)); + } +} diff --git a/buildSrc/src/main/java/github/Deployment.java b/buildSrc/src/main/java/github/Deployment.java new file mode 100644 index 000000000..985e6ed01 --- /dev/null +++ b/buildSrc/src/main/java/github/Deployment.java @@ -0,0 +1,103 @@ +package github; + +import com.google.gson.Gson; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; + +public class Deployment { + + private final Gson mGson; + + private final String username; + private final String password; + + public Deployment(String username, String password) { + this.username = username; + this.password = password; + mGson = GsonCreator.create(); + } + + public Response requestDeployment(Request request) throws Exception { + final String requestJson = mGson.toJson(request); + System.out.println("Request: " + requestJson); + + try (CloseableHttpClient client = HttpClientCreator.create(username, password)) { + HttpPost httpPost = + new HttpPost( + "https://api.github.com/repos/AnySoftKeyboard/AnySoftKeyboard/deployments"); + httpPost.setHeader("Accept", "application/json"); + httpPost.setHeader("Content-type", "application/json"); + httpPost.setEntity(new StringEntity(requestJson, StandardCharsets.UTF_8)); + try (CloseableHttpResponse httpResponse = client.execute(httpPost)) { + System.out.println("Response status: " + httpResponse.getStatusLine()); + return mGson.fromJson( + new InputStreamReader(httpResponse.getEntity().getContent()), + Response.class); + } + } + } + + public static class Request { + public final String ref; + public final String task; + public final boolean auto_merge; + public final String environment; + public final String description; + public final List<String> required_contexts; + public final RequestPayloadField payload; + + public Request( + String ref, + String task, + boolean auto_merge, + String environment, + String description, + List<String> required_contexts, + RequestPayloadField payload) { + this.ref = ref; + this.task = task; + this.auto_merge = auto_merge; + this.environment = environment; + this.description = description; + this.required_contexts = required_contexts; + this.payload = payload; + } + } + + public static class RequestPayloadField { + public final List<String> environments_to_kill; + + public RequestPayloadField(List<String> environmentsToKill) { + environments_to_kill = environmentsToKill; + } + } + + public static class Response { + public final String id; + public final String sha; + public final String ref; + public final String task; + public final RequestPayloadField payload; + public final String environment; + + public Response( + String id, + String sha, + String ref, + String task, + RequestPayloadField payload, + String environment) { + this.id = id; + this.sha = sha; + this.ref = ref; + this.task = task; + this.payload = payload; + this.environment = environment; + } + } +} diff --git a/buildSrc/src/main/java/github/GsonCreator.java b/buildSrc/src/main/java/github/GsonCreator.java new file mode 100644 index 000000000..c5b4077c9 --- /dev/null +++ b/buildSrc/src/main/java/github/GsonCreator.java @@ -0,0 +1,10 @@ +package github; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +class GsonCreator { + public static Gson create() { + return new GsonBuilder().create(); + } +} diff --git a/buildSrc/src/main/java/github/HttpClientCreator.java b/buildSrc/src/main/java/github/HttpClientCreator.java new file mode 100644 index 000000000..86a2b1a34 --- /dev/null +++ b/buildSrc/src/main/java/github/HttpClientCreator.java @@ -0,0 +1,15 @@ +package github; + +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; + +class HttpClientCreator { + public static CloseableHttpClient create(String username, String password) { + BasicCredentialsProvider creds = new BasicCredentialsProvider(); + creds.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password)); + return HttpClientBuilder.create().setDefaultCredentialsProvider(creds).build(); + } +} diff --git a/deployment/build.gradle b/deployment/build.gradle new file mode 100644 index 000000000..45545a46c --- /dev/null +++ b/deployment/build.gradle @@ -0,0 +1,47 @@ +import java.time.DayOfWeek +import java.time.Instant + +apply plugin: 'anysoftkeyboard-deployment' + +deployments { + imeMaster { + environmentSteps = ['alpha_100', 'beta_100'] + } + imeProduction { + environmentSteps = ['production_010', 'production_020', 'production_030', 'production_040', 'production_050', 'production_075', 'production_100'] + } + addOns { + environmentSteps = ['beta_100', 'production_050', 'production_100'] + } +} + +// start - IME +tasks.register("imeOnMasterPush").configure { + it.group "Publishing" + it.description "Deployment request on master push for IME" + it.dependsOn tasks.named("deploymentRequest_imeMaster_alpha_100") +} + +tasks.register("imePromoteMaster").configure { + it.group "Publishing" + it.description "Deployment promoting request on master for IME" + //promoting ime only on Wednesday + it.enabled Instant.now().toCalendar().toDayOfWeek() == DayOfWeek.WEDNESDAY + it.dependsOn tasks.named("deploymentRequest_imeMaster_beta_100") +} +//TODO: release branch flows +// end - IME + +// start - addons +tasks.register("addOnsOnMasterPush").configure { + it.group "Publishing" + it.description "Deployment request on master push for all addons" + it.dependsOn tasks.named("deploymentRequest_addOns_beta_100") +} +tasks.register("addOnsPromoteMaster").configure { + it.group "Publishing" + it.description "NO-OP: Deployment request on master push for all addons" + //it.dependsOn tasks.named("deploymentRequest_addOns_beta_100") +} +//TODO: release branch flows +// end - addons diff --git a/gradle/apk_module.gradle b/gradle/apk_module.gradle index dd22cd68e..6521b2946 100644 --- a/gradle/apk_module.gradle +++ b/gradle/apk_module.gradle @@ -96,7 +96,7 @@ if (project.ext.shouldBePublished) { println("Locale " + Locale.getDefault()) println("file encoding " + CharsetToolkit.defaultSystemCharset) println("File contents:") - println("***" + playStoreWhatsNewFile.text + "***") + println("***" + playStoreWhatsNewFile.text + "***") throw new IllegalStateException("whatsnew file can not be longer than 500 characters! Currently " + playStoreWhatsNewFile.text.length()) } } else { diff --git a/scripts/ci/ci_setup.sh b/scripts/ci/ci_setup.sh index 9384fa9cd..91448a91a 100755 --- a/scripts/ci/ci_setup.sh +++ b/scripts/ci/ci_setup.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash #accepting licenses - creating a folder to store the license CRC -rm -f "${ANDROID_HOME}/licenses" || true +rm -rf "${ANDROID_HOME}/licenses" || true mkdir -p "${ANDROID_HOME}/licenses" #this value was taken from my local machine, after I accepted it locally. echo -e "8933bad161af4178b1185d1a37fbf41ea5269c55\nd56f5187479451eabf01fb78af6dfcb131a6481e\n24333f8a63b6825ea9c5514f83c2829b004d1fee\c" > "${ANDROID_HOME}/licenses/android-sdk-license" diff --git a/scripts/retry-on-SIGSEGV.sh b/scripts/retry-on-SIGSEGV.sh index a9aa5dcd8..c8f71f76d 100755 --- a/scripts/retry-on-SIGSEGV.sh +++ b/scripts/retry-on-SIGSEGV.sh @@ -6,10 +6,14 @@ shift echo "Will retry '$*' for ${retries} times:" function needsRetry() { - local contents - contents=$(cat "$(ls -t build-logging/*.log | head -1)") + if [[ -d "build-logging" ]]; then + local contents + contents=$(cat "$(ls -t build-logging/*.log | head -1)") - [[ "$contents" =~ .*"finished with non-zero exit value 134".* ]] && echo "RETRY" + [[ "$contents" =~ .*"finished with non-zero exit value 134".* ]] && echo "RETRY" + else + echo "RETRY-NO-LOGGING-FOLDER" + fi } set +e diff --git a/settings.gradle b/settings.gradle index c49ce8a4d..c537e6d59 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,5 @@ rootProject.name = 'AnySoftKeyboard' +include ':deployment' include ':api' |
