← Back to team overview

canonical-ubuntu-qa team mailing list archive

[Merge] qa-jenkins-jobs:pre-commit-hooks into qa-jenkins-jobs:master

 

Ural Tunaboyu has proposed merging qa-jenkins-jobs:pre-commit-hooks into qa-jenkins-jobs:master.

Requested reviews:
  Canonical Platform QA Team (canonical-platform-qa)

For more details, see:
https://code.launchpad.net/~canonical-platform-qa/qa-jenkins-jobs/+git/qa-jenkins-jobs/+merge/471372

Added initial pre-commit hooks, to be expanded if/as we think of more. Currently there are three steps: yamllint, shellcheck, and deploy-jobs.

There are two separate yamllint config files, one for all the jobs.yaml files and macros.yaml, and one for all other yaml files. The first yamllint config is much more relaxed since the jobs.yaml files have nonstandard formatting.

Shellcheck ignores a few errors by default (currently): two to do with linking to external sources (since external sources are used in a few scripts and are not in this repo), and one for ignoring the pattern of `VAR=$(echo $VARNAME | sed regex)` as the replacement pattern of `VAR=${VARNAME//search/replace}` does not work with advanced regexes. 

`deploy-jobs` runs `scripts/deploy-jobs.sh test` and makes sure it is passing. Consequently, jobs/auto-upgrade-testing/profiles has been changed to be the result of running `deploy-jobs.sh test`. Otherwise that file will be dirtied every time this pre-commit hook is run. 
-- 
Your team Canonical Platform QA Team is requested to review the proposed merge of qa-jenkins-jobs:pre-commit-hooks into qa-jenkins-jobs:master.
diff --git a/.launchpad.yaml b/.launchpad.yaml
index 7272a90..7929786 100644
--- a/.launchpad.yaml
+++ b/.launchpad.yaml
@@ -1,4 +1,6 @@
+---
 pipeline:
+  - pre_commit
   - jenkins_job_builder
 
 jobs:
@@ -9,4 +11,3 @@ jobs:
       - jenkins-job-builder
       - git
     run: ./scripts/deploy-jobs.sh test
-
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..7bff89d
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,27 @@
+---
+repos:
+  - repo: https://github.com/adrienverge/yamllint.git
+    rev: v1.29.0
+    hooks:
+      - id: yamllint
+        args: [--strict]
+  - repo: https://github.com/adrienverge/yamllint.git
+    rev: v1.29.0
+    hooks:
+      - id: yamllint
+        alias: yamllint-jobs
+        name: yamllint (for jobs.yaml files)
+        args: [--strict, -c, .yamllint-jobs]
+  - repo: https://github.com/shellcheck-py/shellcheck-py
+    rev: v0.9.0.5
+    hooks:
+      - id: shellcheck
+        args: ['-e', 'SC1090', '-e', 'SC1091', '-e', 'SC2001']
+  - repo: local
+    hooks:
+      - id: deploy-jobs
+        name: deploy-jobs
+        entry: scripts/deploy-jobs.sh
+        args:
+          - "test"
+        language: script
diff --git a/.yamllint b/.yamllint
new file mode 100644
index 0000000..c02c276
--- /dev/null
+++ b/.yamllint
@@ -0,0 +1,6 @@
+---
+extends: default
+
+ignore:
+  - "**/*jobs*.yaml"
+  - "jobs/macros.yaml"
diff --git a/.yamllint-jobs b/.yamllint-jobs
new file mode 100644
index 0000000..45421e4
--- /dev/null
+++ b/.yamllint-jobs
@@ -0,0 +1,12 @@
+---
+extends: default
+
+ignore:
+  - "**/*.yaml"
+  - "!**/*jobs*.yaml"
+  - "!jobs/macros.yaml"
+
+rules:
+  indentation: disable
+  line-length: disable
+  document-start: disable
\ No newline at end of file
diff --git a/jobs/auto-upgrade-testing/profiles b/jobs/auto-upgrade-testing/profiles
index a09fbe4..70e06fb 100644
--- a/jobs/auto-upgrade-testing/profiles
+++ b/jobs/auto-upgrade-testing/profiles
@@ -1,24 +1,44 @@
+        - ubuntu-noble-oracular-basic-amd64_qemu
+        - ubuntu-focal-noble-server-amd64_qemu
+        - ubuntu-bionic-noble-server-amd64_qemu
+        - ubuntu-focal-jammy-basic-amd64_qemu
+        - ubuntu-mantic-noble-kubuntu-amd64_qemu
+        - ubuntu-xenial-noble-server-amd64_qemu
+        - ubuntu-jammy-noble-lubuntu-amd64_qemu
+        - ubuntu-noble-oracular-server-amd64_qemu
+        - ubuntu-jammy-noble-server-amd64_qemu
         - ubuntu-bionic-focal-basic-amd64_qemu
-        - ubuntu-kinetic-lunar-desktop-amd64_qemu
-        - ubuntu-xenial-bionic-desktop-i386_qemu
+        - ubuntu-noble-oracular-ubuntu-unity-amd64_qemu
+        - ubuntu-mantic-noble-ubuntustudio-amd64_qemu
         - ubuntu-bionic-focal-desktop-amd64_qemu
-        - ubuntu-focal-jammy-basic-amd64_qemu
-        - ubuntu-kinetic-lunar-lubuntu-amd64_qemu
-        - ubuntu-jammy-kinetic-lubuntu-amd64_qemu
-        - ubuntu-xenial-bionic-desktop-amd64_qemu
-        - ubuntu-xenial-bionic-basic-amd64_qemu
-        - ubuntu-jammy-kinetic-basic-amd64_qemu
-        - ubuntu-kinetic-lunar-ubuntu-mate-amd64_qemu
+        - ubuntu-jammy-noble-kubuntu-amd64_qemu
+        - ubuntu-mantic-noble-xubuntu-amd64_qemu
+        - ubuntu-noble-oracular-ubuntustudio-amd64_qemu
+        - ubuntu-mantic-noble-basic-amd64_qemu
+        - ubuntu-xenial-noble-desktop-amd64_qemu
+        - ubuntu-noble-oracular-kubuntu-amd64_qemu
+        - ubuntu-mantic-noble-ubuntucinnamon-amd64_qemu
         - ubuntu-focal-jammy-kubuntu-amd64_qemu
-        - ubuntu-kinetic-lunar-xubuntu-amd64_qemu
-        - ubuntu-xenial-bionic-basic-i386_qemu
+        - ubuntu-bionic-noble-desktop-amd64_qemu
+        - ubuntu-mantic-noble-lubuntu-amd64_qemu
+        - ubuntu-mantic-noble-server-amd64_qemu
+        - ubuntu-focal-jammy-lubuntu-amd64_qemu
+        - ubuntu-focal-noble-desktop-amd64_qemu
+        - ubuntu-focal-jammy-xubuntu-amd64_qemu
+        - ubuntu-noble-oracular-lubuntu-amd64_qemu
+        - ubuntu-noble-oracular-ubuntu-mate-amd64_qemu
+        - ubuntu-mantic-noble-ubuntu-mate-amd64_qemu
+        - ubuntu-jammy-noble-ubuntu-unity-amd64_qemu
+        - ubuntu-noble-oracular-ubuntucinnamon-amd64_qemu
+        - ubuntu-mantic-noble-ubuntu-unity-amd64_qemu
+        - ubuntu-jammy-noble-basic-amd64_qemu
         - ubuntu-focal-jammy-ubuntu-mate-amd64_qemu
-        - ubuntu-kinetic-lunar-kubuntu-amd64_qemu
+        - ubuntu-jammy-noble-ubuntustudio-amd64_qemu
+        - ubuntu-jammy-noble-ubuntu-mate-amd64_qemu
+        - ubuntu-mantic-noble-desktop-amd64_qemu
+        - ubuntu-noble-oracular-desktop-amd64_qemu
         - ubuntu-focal-jammy-desktop-amd64_qemu
-        - ubuntu-jammy-kinetic-desktop-amd64_qemu
-        - ubuntu-focal-jammy-xubuntu-amd64_qemu
-        - ubuntu-jammy-kinetic-ubuntu-mate-amd64_qemu
-        - ubuntu-jammy-kinetic-xubuntu-amd64_qemu
-        - ubuntu-jammy-kinetic-kubuntu-amd64_qemu
-        - ubuntu-focal-jammy-lubuntu-amd64_qemu
-        - ubuntu-kinetic-lunar-basic-amd64_qemu
+        - ubuntu-jammy-noble-desktop-amd64_qemu
+        - ubuntu-focal-jammy-ubuntustudio-amd64_qemu
+        - ubuntu-noble-oracular-xubuntu-amd64_qemu
+        - ubuntu-jammy-noble-xubuntu-amd64_qemu
diff --git a/jobs/autopkgtest-cloud/jobs.yaml b/jobs/autopkgtest-cloud/jobs.yaml
index d9beada..11fbd8c 100644
--- a/jobs/autopkgtest-cloud/jobs.yaml
+++ b/jobs/autopkgtest-cloud/jobs.yaml
@@ -88,9 +88,9 @@
                 printf "Changes have been made to docs since last built version, building on readthedocs...\n"
                 curl -sS -X POST -H "Authorization: Token ${RTD_TOKEN}" https://readthedocs.org/api/v3/projects/autopkgtest-cloud/versions/latest/builds/
                 printf "readthedocs build request submitted\n"
-            else    
+            else
                 printf "No changes to docs/ since latest build, not building\n"
-            fi      
+            fi
 
 - job:
     name: autodep8-branch-check
diff --git a/jobs/defaults.yaml b/jobs/defaults.yaml
index f26d19c..cfa51a6 100644
--- a/jobs/defaults.yaml
+++ b/jobs/defaults.yaml
@@ -1,9 +1,10 @@
+---
 - defaults:
     name: global
     properties:
-        - build-discarder:
-            days-to-keep: 20
-            num-to-keep: 20
-            artifact-days-to-keep: 20
-            artifact-num-to-keep: 20
+      - build-discarder:
+          days-to-keep: 20
+          num-to-keep: 20
+          artifact-days-to-keep: 20
+          artifact-num-to-keep: 20
     hooks: ''
diff --git a/jobs/iso-testing/jobs-server.yaml b/jobs/iso-testing/jobs-server.yaml
index 82b191b..3197f45 100644
--- a/jobs/iso-testing/jobs-server.yaml
+++ b/jobs/iso-testing/jobs-server.yaml
@@ -291,7 +291,7 @@
         - email-canonical-ubuntu-qa
 
 
-- builder: # This step runs iso static validation
+- builder:  # This step runs iso static validation
     name: validate-iso-server
     builders:
         - shell: |
diff --git a/jobs/iso-testing/jobs.yaml b/jobs/iso-testing/jobs.yaml
index 886c189..c0b0fe8 100644
--- a/jobs/iso-testing/jobs.yaml
+++ b/jobs/iso-testing/jobs.yaml
@@ -25,16 +25,16 @@
 
 - project:
     name: 'iso-testing'
-    flavor: # Additional flavors can be added here
+    flavor:  # Additional flavors can be added here
         - 'ubuntu'
     variant: 'desktop'
-    release: # Additional releases can be added here
+    release:  # Additional releases can be added here
         - 'jammy'
-    supported-release: # All non-dev releases which should be tested go here
+    supported-release:  # All non-dev releases which should be tested go here
         - 'jammy'
-    arch: # Mostly likely different jobs would be needed for other arches
+    arch:  # Mostly likely different jobs would be needed for other arches
         - 'amd64'
-    desktop-test: # Currently this is all non-default desktop test cases
+    desktop-test:  # Currently this is all non-default desktop test cases
         - 'default_de'
         - 'health-check'
         - 'lvm'
@@ -74,7 +74,7 @@
         - '{flavor}-{release}-{variant}-{arch}-smoke-{desktop-test}'
 
 # builder stanzas describe reusable build steps
-- builder: # This step downloads the iso
+- builder:  # This step downloads the iso
     name: download-iso
     builders:
         - shell: |
@@ -101,14 +101,14 @@
             rm -f "$iso.zs-old"
 
             chmod 644 "$iso"
-- builder: # This step runs a specified iso test
+- builder:  # This step runs a specified iso test
     name: run-iso-test
     builders:
         - shell: |
             cd qa-jenkins-jobs
             ./scripts/iso-testing/run-iso-test.sh --test {test} --release {release} --variant {variant} --arch {arch}
 
-- builder: # This step runs iso static validation
+- builder:  # This step runs iso static validation
     name: validate-iso
     builders:
         - shell: |
@@ -119,7 +119,7 @@
             sudo -u utah -i /usr/share/utah/isotest/iso_static_validation.py --name {iso}
             sudo -u utah -i cp {iso} /var/cache/utah/iso/{release}-{variant}-{arch}.iso
 
-- builder: # This step writes a parameters file for the mark-pending-current job
+- builder:  # This step writes a parameters file for the mark-pending-current job
     name: write-parameters-file
     builders:
         - shell: |
@@ -129,8 +129,9 @@
             echo "TYPE={variant}" >> mark-current-parameters
             echo "PROJECT={flavor}" >> mark-current-parameters
 
-- parameter: # This parameter inserts a wait between the url update and the
-             # download so that files can all sync
+- parameter:
+    # This parameter inserts a wait between the url update and the
+    # download so that files can all sync
     name: download-sleep
     parameters:
         - string:
@@ -138,8 +139,9 @@
             default: '300'
             description: 'Time to sleep to wait for files to sync after url updates'
 
-- publisher: # This publisher triggers the job to mark a pending image as
-             # current after an iso test succeeds
+- publisher:
+    # This publisher triggers the job to mark a pending image as
+    # current after an iso test succeeds
     name: 'trigger-mark-pending-current'
     publishers:
         - trigger-parameterized-builds:
@@ -147,8 +149,9 @@
               property-file: 'mark-current-parameters'
               condition: 'SUCCESS'
 
-- publisher: # This publisher records unstable results when utah fails without
-             # an actual test failure
+- publisher:
+    # This publisher records unstable results when utah fails without
+    # an actual test failure
     name: 'mark-junit'
     publishers:
         - junit:
@@ -187,11 +190,12 @@
     parameters:
         - download-sleep
     node: 'iso-testing'
-    triggers: # Check the image manifest and file list for changes every every 15 minutes
-    #        - daily
-# Currently working with IS to determine the best method - https://portal.admin.canonical.com/101149
+    triggers:
+        # Check the image manifest and file list for changes every every 15 minutes
+        # - daily
+        # Currently working with IS to determine the best method - https://portal.admin.canonical.com/101149
         - pollurl:
-            cron: 'H/15 * * * *' # The H allows jenkins to stagger jobs using a hash
+            cron: 'H/15 * * * *'  # The H allows jenkins to stagger jobs using a hash
             polling-node: 'iso-testing'
             urls:
                 - url: !j2: "{{ {'desktop': 'http://cdimage.ubuntu.com/daily-live/pending/SHA256SUMS'}[variant] }}"
@@ -221,11 +225,12 @@
     parameters:
         - download-sleep
     node: 'iso-testing'
-    triggers: # Check the image manifest and file list for changes every every 15 minutes
-    #        - daily
-# Currently working with IS to determine the best method - https://portal.admin.canonical.com/101149
+    triggers:
+        # Check the image manifest and file list for changes every every 15 minutes
+        # - daily
+        # Currently working with IS to determine the best method - https://portal.admin.canonical.com/101149
         - pollurl:
-            cron: 'H/15 * * * *' # The H allows jenkins to stagger jobs using a hash
+            cron: 'H/15 * * * *'  # The H allows jenkins to stagger jobs using a hash
             polling-node: 'iso-testing'
             urls:
                 - url: 'http://cdimage.ubuntu.com/{supported-release}/daily-live/pending/SHA256SUMS'
@@ -254,7 +259,7 @@
         Defined in lp:qa-jenkins-jobs
     node: 'iso-testing'
     triggers:
-        - reverse: # Trigger after the download job succeeds
+        - reverse:  # Trigger after the download job succeeds
             jobs: '{flavor}-{release}-{variant}-{arch}-iso-download'
             result: 'success'
     wrappers:
@@ -287,7 +292,7 @@
           default: lp:qa-jenkins-jobs
           description: "Launchpad branch of qa-jenkins-jobs from which to pick the scripts from."
     triggers:
-        - reverse: # Trigger after static validation succeeds
+        - reverse:  # Trigger after static validation succeeds
             jobs: '{flavor}-{release}-{variant}-{arch}-iso-static-validation'
             result: 'success'
     wrappers:
@@ -334,7 +339,7 @@
           default: lp:qa-jenkins-jobs
           description: "Launchpad branch of qa-jenkins-jobs from which to pick the scripts from."
     triggers:
-        - reverse: # Trigger after the default test succeeds
+        - reverse:  # Trigger after the default test succeeds
             jobs: '{flavor}-{release}-{variant}-{arch}-smoke-default'
             result: 'success'
     wrappers:
diff --git a/jobs/macros.yaml b/jobs/macros.yaml
index 3d3a175..76aca0d 100644
--- a/jobs/macros.yaml
+++ b/jobs/macros.yaml
@@ -15,19 +15,19 @@
 # You should have received a copy of the GNU General Public License along
 # with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-- builder: # This step removes artifacts from previous builds
+- builder:  # This step removes artifacts from previous builds
     name: clear-artifacts
     builders:
         - shell: 'sudo rm -rf *'
 
-- builder: # Install requested packages
+- builder:  # Install requested packages
     name: package-installer
     builders:
       - shell: |
             sudo apt-get update
             sudo apt-get install -y --assume-yes {to-install}
 
-- builder: # This step writes the iso info to the output
+- builder:  # This step writes the iso info to the output
     name: echo-media-info
     builders:
         - shell: |
@@ -102,14 +102,14 @@
             source $WORKSPACE/grafana.conf
             $GRAFANA_INTEGRATION_PATH/run_script sync-tests -rt {results_type} -rf {results_file} -if {info_file} -m tests_01 -t $(date +%s)
 
-- publisher: # This publisher archives all job artifacts
+- publisher:  # This publisher archives all job artifacts
     name: 'archive-artifacts'
     publishers:
         - archive:
             artifacts: '**/*'
             allow-empty: 'true'
 
-- trigger: # This will schedule a daily build at a staggered time
+- trigger:  # This will schedule a daily build at a staggered time
     name: 'daily'
     triggers:
         - timed: 'H H * * *'
diff --git a/scripts/auto-upgrade-testing/set-common-variables.sh b/scripts/auto-upgrade-testing/set-common-variables.sh
index 3a8917d..f07e88e 100755
--- a/scripts/auto-upgrade-testing/set-common-variables.sh
+++ b/scripts/auto-upgrade-testing/set-common-variables.sh
@@ -5,7 +5,7 @@ set -u
 usage() {
     # Display script usage
     cat<<EOF
-Usage: $(basename $0) [OPTIONS...]
+Usage: $(basename "$0") [OPTIONS...]
     Set common variables from a profile path
 Options:
     -h, --help     This help.
@@ -22,7 +22,7 @@ while true ; do
         -h|--help)
             usage;;
         -p|--profile)
-            profile=$2
+            profile="$2"
             shift 2;;
         --) shift ; break ;;
         *) usage;;
@@ -32,17 +32,17 @@ done
 
 # Construct profile usage
 export PROFILE_NAME="$profile.yaml"
-export PROFILE=$WORKSPACE/auto-upgrade-testing-specifications/profiles/$PROFILE_NAME
+export PROFILE="$WORKSPACE"/auto-upgrade-testing-specifications/profiles/"$PROFILE_NAME"
 
-FLAVOR=$(grep flavor $PROFILE | sed 's/^.*flavor: //')
-ARCH=$(grep arch $PROFILE | sed 's/^.*arch: //')
-IMAGE_NAME=$(grep image_name $PROFILE | sed 's/^.*image_name: //')
-FROM_RELEASE=$(grep -m1 -A1 "releases:" $PROFILE | tail -1 | grep -o "\"[^\"]*\"" | tr -d '"')
-TO_RELEASE=$(grep -m1 -A2 "releases:" $PROFILE | tail -1 | grep -o "\"[^\"]*\"" | tr -d '"')
+FLAVOR=$(grep flavor "$PROFILE" | sed 's/^.*flavor: //')
+ARCH=$(grep arch "$PROFILE" | sed 's/^.*arch: //')
+IMAGE_NAME=$(grep image_name "$PROFILE" | sed 's/^.*image_name: //')
+FROM_RELEASE=$(grep -m1 -A1 "releases:" "$PROFILE" | tail -1 | grep -o "\"[^\"]*\"" | tr -d '"')
+TO_RELEASE=$(grep -m1 -A2 "releases:" "$PROFILE" | tail -1 | grep -o "\"[^\"]*\"" | tr -d '"')
 
-rm -f $WORKSPACE/common-vars
+rm -f "$WORKSPACE"/common-vars
 
-cat >> $WORKSPACE/common-vars <<EOF
+cat >> "$WORKSPACE"/common-vars <<EOF
 FLAVOR=$FLAVOR
 ARCH=$ARCH
 FROM_RELEASE=$FROM_RELEASE
@@ -50,4 +50,4 @@ TO_RELEASE=$TO_RELEASE
 PROFILE=$PROFILE
 IMAGE_NAME=$IMAGE_NAME
 EOF
-cat $WORKSPACE/common-vars
+cat "$WORKSPACE"/common-vars
diff --git a/scripts/deploy-jobs.sh b/scripts/deploy-jobs.sh
index 20a3cbe..a751454 100755
--- a/scripts/deploy-jobs.sh
+++ b/scripts/deploy-jobs.sh
@@ -12,4 +12,4 @@ esac
 
 make -C "$JOB_PATH" clean
 make -C "$JOB_PATH"
-jenkins-jobs $ARGS -r "$JOB_PATH"
+jenkins-jobs "$ARGS" -r "$JOB_PATH"
diff --git a/scripts/desktop/run_testflinger.sh b/scripts/desktop/run_testflinger.sh
index b2c7ae3..7075170 100755
--- a/scripts/desktop/run_testflinger.sh
+++ b/scripts/desktop/run_testflinger.sh
@@ -1,26 +1,26 @@
 #!/bin/bash
-
+# shellcheck disable=SC1039
 set +e -x
 
 # Ensure testflinger-cli is installed
-if [ ! $(which testflinger-cli) ]; then
-    cd $WORKSPACE
+if [ ! "$(which testflinger-cli)" ]; then
+    cd "$WORKSPACE" || exit
     git clone https://git.launchpad.net/testflinger-cli
-    cd testflinger-cli
+    cd testflinger-cli || exit
     virtualenv -p python3 env
     . env/bin/activate
     if [ -f ~/.proxy_info ] ; then
         source ~/.proxy_info
         export HTTPS_PROXY=$HTTPS_PROXY
-        sudo pip install . --proxy=$HTTPS_PROXY
+        sudo pip install . --proxy="$HTTPS_PROXY"
     else
         sudo pip install .
     fi
 
-    cd $WORKSPACE
+    cd "$WORKSPACE" || exit
 fi
 
-cat > $WORKSPACE/job.yaml <<EOF
+cat > "$WORKSPACE"/job.yaml <<EOF
 job_queue: $DEVICE_QUEUE
 provision_data:
   distro: $RELEASE
@@ -65,12 +65,12 @@ test_data:
     mkdir -p artifacts && sudo cp -R /tmp/ust_artifacts/* artifacts
     sudo chown -R ubuntu:ubuntu artifacts
 EOF
-JOB_ID=$(testflinger-cli submit -q $WORKSPACE/job.yaml)
+JOB_ID=$(testflinger-cli submit -q "$WORKSPACE"/job.yaml)
 echo "JOB_ID: ${JOB_ID}"
-cd ${WORKSPACE}
-testflinger-cli poll ${JOB_ID}
+cd "${WORKSPACE}" || exit
+testflinger-cli poll "${JOB_ID}"
 # Sometimes the artifacts are not available right away testflinger job finishes, so we sleep a bit.
 sleep 15s
-testflinger-cli artifacts ${JOB_ID}
+testflinger-cli artifacts "${JOB_ID}"
 tar -xzf artifacts.tgz
 mv artifacts results
diff --git a/scripts/flash-device.sh b/scripts/flash-device.sh
index 1efa0db..9dbbbb8 100755
--- a/scripts/flash-device.sh
+++ b/scripts/flash-device.sh
@@ -22,15 +22,17 @@
 # Added it to power tests to separate out the flashing code from the rest.
 
 # extract device name from node name (there's probably a more sh way to do this)
-export DEVICE=$(echo $NODE_NAME | sed 's/\(\w\)-.*/\1/')
+DEVICE=$(echo "$NODE_NAME" | sed 's/\(\w\)-.*/\1/')
+export DEVICE
 
 # need plars' lifeboat scripts to get our adb serial
 rm -rf lifeboat
-if [ -z LIFEBOAT_BRANCH ]; then
+if [ -z "$LIFEBOAT_BRANCH" ]; then
     LIFEBOAT_BRANCH="https://git.launchpad.net/~canonical-hw-cert/lifeboat";
 fi
-git clone $LIFEBOAT_BRANCH
-export ANDROID_SERIAL=$(./lifeboat/get-device-info serial $NODE_NAME)
+git clone "$LIFEBOAT_BRANCH"
+ANDROID_SERIAL=$(./lifeboat/get-device-info serial "$NODE_NAME")
+export ANDROID_SERIAL
 
 export PASSWD="0000"
 export DEVMODE="--developer-mode --password=$PASSWD"
@@ -74,67 +76,67 @@ echo "Wifi: $WIFI"
 echo "Fastboot: $FASTBOOT"
 echo "Recovery: $RECOVERY"
 QUERY_CMD="ubuntu-device-flash $SERVER $REVARGS query --channel=$CHANNEL --device $DEVICE --show-image"
-echo $QUERY_CMD
+echo "$QUERY_CMD"
 $QUERY_CMD
 
 rootcmd () {
-  adb -s $ANDROID_SERIAL shell "echo $PASSWD | sudo -S $*"
+  adb -s "$ANDROID_SERIAL" shell "echo $PASSWD | sudo -S $*"
 }
 
 # Handle fastboot mode and fastboot recovery mode
 if [ x"$FASTBOOT_RECOVERY" != x ]; then
   echo 'Rebooting to bootloader/fastboot'
-  adb -s $ANDROID_SERIAL reboot bootloader
+  adb -s "$ANDROID_SERIAL" reboot bootloader
   sleep 10
   echo "Flashing recovery image $FASTBOOT_RECOVERY"
-  fastboot -s $ANDROID_SERIAL flash recovery $FASTBOOT_RECOVERY
+  fastboot -s "$ANDROID_SERIAL" flash recovery "$FASTBOOT_RECOVERY"
   echo "Rebooting into recovery"
-  fastboot -s $ANDROID_SERIAL boot $FASTBOOT_RECOVERY
+  fastboot -s "$ANDROID_SERIAL" boot "$FASTBOOT_RECOVERY"
   sleep 10
   FASTBOOT=no
-elif [ x"$FASTBOOT" = xyes ]; then
+elif [ "$FASTBOOT" = xyes ]; then
   EXTRA="--bootstrap"
   echo 'Rebooting to bootloader/fastboot'
-  adb -s $ANDROID_SERIAL reboot bootloader
+  adb -s "$ANDROID_SERIAL" reboot bootloader
   sleep 10
 fi
 
 echo 'flashing device'
 FLASH_CMD="ubuntu-device-flash $SERVER $REVARGS touch --serial=$ANDROID_SERIAL --channel=$CHANNEL --device $DEVICE $DEVMODE $RECOVERY --wipe $EXTRA"
-echo $FLASH_CMD
+echo "$FLASH_CMD"
 $FLASH_CMD
 
 echo 'waiting for device to boot'
-adb -s $ANDROID_SERIAL wait-for-device
+adb -s "$ANDROID_SERIAL" wait-for-device
 echo 'waiting a few seconds for boot'
 sleep 5
 
 # setup network; location of config per plars' advice
 DEFAULT_NETWORK_CONFIG_PATH="/var/lib/jenkins/.ubuntu-ci/wifi.conf"
-if [ -n NETWORK_CONFIG_PATH ]; then
+if [ -n "$NETWORK_CONFIG_PATH" ]; then
   NETWORK_CONFIG_PATH=$DEFAULT_NETWORK_CONFIG_PATH
 fi
-phablet-network -n $NETWORK_CONFIG_PATH
+phablet-network -n "$NETWORK_CONFIG_PATH"
 
 # Disable the welcome wizard
-if [ x"$WIZARD" = x"no" ]; then
+if [ "$WIZARD" = x"no" ]; then
   echo 'disabling wizard'
-  phablet-config -s $ANDROID_SERIAL welcome-wizard --disable
-  adb -s $ANDROID_SERIAL wait-for-device
+  phablet-config -s "$ANDROID_SERIAL" welcome-wizard --disable
+  adb -s "$ANDROID_SERIAL" wait-for-device
   REBOOT_NEEDED=yes
 fi
 
 # Disable the edges intro
-if [ x"$EDGEINTRO" = x"no" ]; then
+if [ "$EDGEINTRO" = x"no" ]; then
   echo 'disabling edges intro'
-  phablet-config -s $ANDROID_SERIAL edges-intro --disable
-  adb -s $ANDROID_SERIAL wait-for-device
+  phablet-config -s "$ANDROID_SERIAL" edges-intro --disable
+  adb -s "$ANDROID_SERIAL" wait-for-device
 fi
 
-if [ x"$REBOOT_NEEDED" = x"yes" ]; then
+if [ "$REBOOT_NEEDED" = x"yes" ]; then
   echo 'rebooting to activate previous changes'
-  adb -s $ANDROID_SERIAL reboot
-  adb -s $ANDROID_SERIAL wait-for-device
+  adb -s "$ANDROID_SERIAL" reboot
+  adb -s "$ANDROID_SERIAL" wait-for-device
 fi
 
 # wait until our network is ready
@@ -142,7 +144,7 @@ echo "Checking device network connection..."
 RETRY=0;
 until wget -q --spider launchpad.net || [ $RETRY -gt 30 ]; do
   sleep 1
-  RETRY=$(($RETRY+1))
+  RETRY=$((RETRY+1))
   echo "wget attempt $RETRY failed, trying again..."
 done
 if [ $RETRY -gt 30 ]; then
diff --git a/scripts/get-recovery-image.sh b/scripts/get-recovery-image.sh
index fa0587e..9c32836 100755
--- a/scripts/get-recovery-image.sh
+++ b/scripts/get-recovery-image.sh
@@ -15,7 +15,8 @@
 # You should have received a copy of the GNU General Public License along
 # with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-export DEVICE=$(echo $NODE_NAME | sed 's/\(\w\)-.*/\1/')
+DEVICE=$(echo "$NODE_NAME" | sed 's/\(\w\)-.*/\1/')
+export DEVICE
 
 # from plars' lifeboat scripts, deposit the proper recovery image in ./recovery.img
-wget -O ${WORKSPACE}/recovery.img http://people.canonical.com/~plars/touch/recovery-$DEVICE.img
+wget -O "${WORKSPACE}"/recovery.img http://people.canonical.com/~plars/touch/recovery-"$DEVICE".img
diff --git a/scripts/iso-testing/run-iso-test.sh b/scripts/iso-testing/run-iso-test.sh
index cf82488..5b5025e 100755
--- a/scripts/iso-testing/run-iso-test.sh
+++ b/scripts/iso-testing/run-iso-test.sh
@@ -7,7 +7,7 @@ set -x
 on_exit() {
     # Exit Handler
     if [ -z ${RETCODE} ] || [ ${RETCODE} != 0 ]; then
-        [ ! -z "$machine_name" ] && [ -d "$SHARED_IMG_PATH"/"$machine_name" ] && delete_vm $machine_name
+        [ -n "$machine_name" ] && [ -d "$SHARED_IMG_PATH"/"$machine_name" ] && delete_vm "$machine_name"
         exit "${RETCODE:=1}"
     else
         echo SUCCESS!!!!
@@ -17,7 +17,7 @@ on_exit() {
 
 delete_vm() {
     # $1: machine name to delete
-    sudo virsh destroy $1 && sudo virsh undefine $1
+    sudo virsh destroy "$1" && sudo virsh undefine "$1"
 }
 
 trap on_exit EXIT INT QUIT ABRT PIPE TERM
@@ -25,7 +25,7 @@ trap on_exit EXIT INT QUIT ABRT PIPE TERM
 usage() {
     # Display script usage
     cat<<EOF
-Usage: $(basename $0) [OPTIONS...]
+Usage: $(basename "$0") [OPTIONS...]
     Run a specified iso test using utah
 Options:
     -h, --help     This help.
@@ -83,10 +83,10 @@ if [ "$variant" = "desktop" ] && [ "$release" != "jammy" ] && [ "$release" != "f
     desktop_replace_autoinstall=true
 fi
 
-cd $WORKSPACE
+cd "$WORKSPACE" || exit
 
 #TODO: Packaging for this?
-bzr export scripts lp:ubuntu-test-cases/$variant/scripts
+bzr export scripts lp:ubuntu-test-cases/"$variant"/scripts
 # replace the autoinstall configuration for the new installer
 if [ "$desktop_replace_autoinstall" = true ]; then
     sed -i s#\.cfg#\.yaml#g scripts/*.sh
@@ -113,8 +113,8 @@ cat <<EOF > "$UTAH_CONFIG_FILE"
 }
 EOF
 for file in "$UTAH_LOG_FILE" "$UTAH_DEBUGLOG_FILE" "$UTAH_SSH_LOG_FILE"; do
-    touch $file
-    chmod a+w $file
+    touch "$file"
+    chmod a+w "$file"
 done
 
 if [ -n "$iso" ]; then
@@ -123,12 +123,12 @@ else
     cmd="./scripts/$test.sh -i /var/cache/utah/iso/$isoname"
 fi
 
-if [ ! -z "$machine_prefix" ] ; then
-    machine_name="${machine_prefix}-$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 16 | head -n 1)"
+if [ -n "$machine_prefix" ] ; then
+    machine_name="${machine_prefix}-$(tr -dc 'a-zA-Z0-9' < /dev/urandom | fold -w 16 | head -n 1)"
     cmd="$cmd -n $machine_name"
-    rm -f $WORKSPACE/ust-parameters.txt
+    rm -f "$WORKSPACE"/ust-parameters.txt
     export PROPERTIES_FILE_PATH=$WORKSPACE/ust-parameters.txt
-    cat >> $PROPERTIES_FILE_PATH <<EOF
+    cat >> "$PROPERTIES_FILE_PATH" <<EOF
 SHARED_IMG_PATH=$SHARED_IMG_PATH/$machine_name/disk0.qcow2
 EOF
 fi
@@ -147,10 +147,10 @@ if [ -z "$xml" ]; then
     esac
 fi
 
-sudo -u utah UTAH_CONFIG_DIR=$UTAH_CONFIG_DIR $($cmd) -f /var/log/installer -x /etc/utah/$xml --outputpreseed > $LOG_DIR/utah-server-stdout.log
+sudo -u utah UTAH_CONFIG_DIR="$UTAH_CONFIG_DIR" "$($cmd)" -f /var/log/installer -x /etc/utah/"$xml" --outputpreseed | tee "$LOG_DIR"/utah-server-stdout.log
 RETCODE=$?
 
-[ ! -z "$machine_name" ] && sudo chmod 777 "$SHARED_IMG_PATH"/"$machine_name"/disk0.qcow2
+[ -n "$machine_name" ] && sudo chmod 777 "$SHARED_IMG_PATH"/"$machine_name"/disk0.qcow2
 
 # Make sure syslog file is readable to add it as artifact
 [ -n "$(find "$LOG_DIR" -name '*.syslog.log')" ] && sudo chmod a+r "$LOG_DIR"/*.syslog.log
diff --git a/scripts/recover-device.sh b/scripts/recover-device.sh
index 0453eb8..e0375ae 100755
--- a/scripts/recover-device.sh
+++ b/scripts/recover-device.sh
@@ -19,5 +19,5 @@ set -ex
 
 # use plars' lifeboat scripts to recover this device to a sane state
 rm -rf lifeboat
-git clone $LIFEBOAT_BRANCH
-./lifeboat/recover $NODE_NAME
+git clone "$LIFEBOAT_BRANCH"
+./lifeboat/recover "$NODE_NAME"
diff --git a/scripts/ubuntu-system-tests/report-results-to-practitest.sh b/scripts/ubuntu-system-tests/report-results-to-practitest.sh
index 932e927..a5460c6 100755
--- a/scripts/ubuntu-system-tests/report-results-to-practitest.sh
+++ b/scripts/ubuntu-system-tests/report-results-to-practitest.sh
@@ -18,23 +18,27 @@ python3 setup.py develop
 bzr cat "$UBUNTU_SYSTEM_TESTS_CONFIG_BRANCH/practitest/config.ini" > config.ini
 
 upload_system_practitest () {
-  adb -s $ANDROID_SERIAL shell system-image-cli -i | tr '\r' ' ' > VERSION_INFO
-  export IMAGE=$(grep "version version" VERSION_INFO | sed 's/^version version: //;s/ $//')
-  export DEVICE=$(grep "device name" VERSION_INFO | sed 's/^device name: \(.\)/\u\1/;s/ $//')
-  export RELEASE_NUMBER=$(adb shell lsb_release -rs | tr -d '\r')
-  export RELEASE_NAME=$(adb shell lsb_release -cs | tr -d '\r')
+  adb -s "$ANDROID_SERIAL" shell system-image-cli -i | tr '\r' ' ' > VERSION_INFO
+  IMAGE=$(grep "version version" VERSION_INFO | sed 's/^version version: //;s/ $//')
+  DEVICE=$(grep "device name" VERSION_INFO | sed 's/^device name: \(.\)/\u\1/;s/ $//')
+  RELEASE_NUMBER=$(adb shell lsb_release -rs | tr -d '\r')
+  RELEASE_NAME=$(adb shell lsb_release -cs | tr -d '\r')
+  export IMAGE
+  export DEVICE
+  export RELEASE_NUMBER
+  export RELEASE_NAME
   TEST_LEVEL_LOWER=$1
-  TEST_LEVEL_UPPER=$(echo $1 | sed 's/^\(.\)/\u\1/')
+  TEST_LEVEL_UPPER=$(echo "$1" | sed 's/^\(.\)/\u\1/')
   SUFFIX=$2
   SUBUNIT_FILE=$3
 
-  if [ $1 = "sanity" ] ; then
+  if [ "$1" = "sanity" ] ; then
     export MANUAL="--manual=${TEST_LEVEL_UPPER}/${DEVICE}"
   else
     export MANUAL=""
   fi
 
-  subunit-practitest-export $SUBUNIT_FILE create "${TEST_LEVEL_LOWER}-${DEVICE}-${RELEASE_NAME}-${IMAGE}-${SUFFIX}" ${DEVICE} ${TEST_LEVEL_UPPER} ${RELEASE_NUMBER} ${IMAGE} "'$(adb shell system-image-cli -i)'" ${MANUAL}
+  subunit-practitest-export "$SUBUNIT_FILE" create "${TEST_LEVEL_LOWER}-${DEVICE}-${RELEASE_NAME}-${IMAGE}-${SUFFIX}" "${DEVICE}" "${TEST_LEVEL_UPPER}" "${RELEASE_NUMBER}" "${IMAGE}" "'$(adb shell system-image-cli -i)'" "${MANUAL}"
 
 }
 
@@ -43,7 +47,7 @@ export https_proxy="http://squid.internal:3128/";
 
 # looks like:
 # upload_system_practitest sanity uk "${WORKSPACE}/foo/test-results.subunit"
-upload_system_practitest ${TEST_LEVEL} ${LABEL} ${SUBUNIT_RESULTS_PATH}
+upload_system_practitest "${TEST_LEVEL}" "${LABEL}" "${SUBUNIT_RESULTS_PATH}"
 
 unset https_proxy
 deactivate
diff --git a/scripts/ubuntu-system-tests/write-ust-config.sh b/scripts/ubuntu-system-tests/write-ust-config.sh
index a784fcd..9e7e7b8 100755
--- a/scripts/ubuntu-system-tests/write-ust-config.sh
+++ b/scripts/ubuntu-system-tests/write-ust-config.sh
@@ -5,4 +5,4 @@ set -x
 CONFIG=$1
 FILE="${CONFIG:=config.ini}"
 
-bzr cat ${UBUNTU_SYSTEM_TESTS_CONFIG_BRANCH}/$FILE > "${CONFIG_PATH}"
+bzr cat "${UBUNTU_SYSTEM_TESTS_CONFIG_BRANCH}"/"$FILE" > "${CONFIG_PATH}"

Follow ups