Add the cws-upload and fetch-cws-upload-log roles

Modified versions of these roles were previously hosted in the
infinitegforums project, but these will also be used in the
translateselectedtext project, and thus from now on they will be hosted
here so they can be shared between the two.

In fact, these roles have been modified and documentation has been added
so they can be used by other Zuul users in their own projects.

Change-Id: Ic6f9692d3a087b80bd8ac5bfa8fbb5043bea6f4b
diff --git a/roles/cws-upload/README.md b/roles/cws-upload/README.md
new file mode 100644
index 0000000..460e5aa
--- /dev/null
+++ b/roles/cws-upload/README.md
@@ -0,0 +1,17 @@
+Upload an extension ZIP to the Chrome Web Store using the
+[chrome-webstore-upload][1] program.
+
+## Role variables
+- `extensionId`: extension ID as uploaded in the Chrome Web Store.
+- `clientId`: Google API client ID ([more info][2]).
+- `refreshToken`: Google API refresh token ([more info][2]).
+- `workingDirectory`: directory where the ZIP file is located and where the
+upload logs will be written.
+- `zipFile`: name of the ZIP file which will be uploaded.
+- `autopublish`: whether the updated extension should be published immediately
+after the review completes. (default: `true`)
+- `trustedTesters`: whether the extension should be published only to trusted
+testers.
+
+[1]: https://github.com/fregante/chrome-webstore-upload-cli
+[2]: https://github.com/fregante/chrome-webstore-upload/blob/main/How%20to%20generate%20Google%20API%20keys.md
diff --git a/roles/cws-upload/defaults/main.yaml b/roles/cws-upload/defaults/main.yaml
new file mode 100644
index 0000000..e440e1d
--- /dev/null
+++ b/roles/cws-upload/defaults/main.yaml
@@ -0,0 +1,2 @@
+autopublish: true
+trustedTesters: true
diff --git a/roles/cws-upload/tasks/main.yaml b/roles/cws-upload/tasks/main.yaml
new file mode 100644
index 0000000..aaab7a5
--- /dev/null
+++ b/roles/cws-upload/tasks/main.yaml
@@ -0,0 +1,34 @@
+- name: Check extensionId, clientId, refreshToken, workingDirectory, zipFile are set
+  when: >
+    extensionId is not defined or clientId is not defined or
+    refreshToken is not defined or workingDirectory is not defined or
+    zipFile is not defined
+  fail:
+    msg: "extensionId, clientId, refreshToken, workingDirectory and zipFile must be set"
+
+- name: Upload and publish the ZIP file to the Chrome Web Store
+  ansible.builtin.shell:
+    cmd: |
+      set -o pipefail
+      chrome-webstore-upload upload {{ '--auto-publish' if (autopublish|bool) else '' }} \
+          --extension-id {{ extensionId }} {{ '--trusted-testers' if (trustedTesters|bool) else '' }} \
+          --source {{ zipFile }} --client-id {{ clientId|quote }} \
+          --refresh-token {{ refreshToken|quote }} \
+          2>&1 | tee cws-log.txt
+    chdir: "{{ workingDirectory }}"
+    executable: /bin/bash
+  no_log: True
+  register: uploadcmd
+  failed_when: false
+
+- name: Read upload log
+  ansible.builtin.shell:
+    cmd: cat cws-log.txt
+    chdir: "{{ workingDirectory }}"
+    executable: /bin/bash
+  register: uploadlog
+
+- name: Check whether the upload was successful
+  when: "not (uploadcmd.rc == 0 or ('ITEM_NOT_UPDATABLE' in uploadlog.stdout) or ('PKG_INVALID_VERSION_NUMBER') in uploadlog.stdout)"
+  fail:
+    msg: "{{ uploadlog.stdout }}"