From f5c95d2ba0b142eb1901b018cb7f5eeecbc06cb2 Mon Sep 17 00:00:00 2001 From: Janosh Riebesell Date: Sun, 23 Jan 2022 16:08:23 +0000 Subject: [PATCH] Flynt + shellcheck (#1473) * quote variables in src/gam-install.sh + fix typos * flynt src (auto f-string conversion) * quote all shell variables --- .github/actions/linux-before-install.sh | 19 +++++++++---------- .github/actions/linux-install.sh | 6 +++--- .github/actions/macos-before-install.sh | 6 +++--- .github/actions/macos-install.sh | 6 +++--- .github/actions/windows-before-install.sh | 4 ++-- .github/actions/windows-install.sh | 4 ++-- src/GamCommands.txt | 2 +- src/LICENSE | 2 +- src/gam-install.sh | 22 +++++++++++----------- src/gam/__init__.py | 5 ++--- src/gam/auth/oauth_test.py | 12 ++++++------ src/gam/controlflow_test.py | 4 ++-- src/gam/gapi/errors.py | 7 +++---- src/gam/gapi/vault.py | 3 +-- src/gam/utils.py | 2 +- src/versionhistory-v1.json | 6 +++--- 16 files changed, 53 insertions(+), 57 deletions(-) diff --git a/.github/actions/linux-before-install.sh b/.github/actions/linux-before-install.sh index 66edad25..35fcbddb 100755 --- a/.github/actions/linux-before-install.sh +++ b/.github/actions/linux-before-install.sh @@ -46,16 +46,16 @@ if [ $SSLRESULT -ne 0 ] || [[ "$SSLVER" != "OpenSSL $BUILD_OPENSSL_VERSION "* ]] sudo apt-get -qq --yes update > /dev/null sudo apt-get -qq --yes build-dep python3 > /dev/null fi - + # Compile latest OpenSSL curl -O --silent "https://www.openssl.org/source/openssl-${BUILD_OPENSSL_VERSION}.tar.gz" echo "Extracting OpenSSL..." - tar xf openssl-$BUILD_OPENSSL_VERSION.tar.gz - cd openssl-$BUILD_OPENSSL_VERSION + tar xf openssl-"$BUILD_OPENSSL_VERSION".tar.gz + cd openssl-"$BUILD_OPENSSL_VERSION" echo "Compiling OpenSSL $BUILD_OPENSSL_VERSION..." - ./Configure --libdir=lib --prefix=$HOME/ssl + ./Configure --libdir=lib --prefix="$HOME"/ssl echo "Running make for OpenSSL..." - make -j$cpucount -s + make -j"$cpucount" -s echo "Running make install for OpenSSL..." make install > /dev/null cd ~ @@ -65,22 +65,21 @@ if [ $SSLRESULT -ne 0 ] || [[ "$SSLVER" != "OpenSSL $BUILD_OPENSSL_VERSION "* ]] curl -O --silent "https://www.python.org/ftp/python/${BUILD_PYTHON_VERSION}/Python-${BUILD_PYTHON_VERSION}.tar.xz" echo "Extracting Python..." tar xf "Python-${BUILD_PYTHON_VERSION}.tar.xz" - cd Python-$BUILD_PYTHON_VERSION + cd Python-"$BUILD_PYTHON_VERSION" echo "Compiling Python $BUILD_PYTHON_VERSION..." export flags="--with-openssl=${HOME}/ssl --enable-shared --prefix=${HOME}/python --with-ensurepip=upgrade --enable-optimizations --with-lto" - ./configure $flags > /dev/null - make -j$cpucount -s + ./configure "$flags" > /dev/null + make -j"$cpucount" -s echo "Installing Python..." make install > /dev/null cd ~ fi python="${HOME}/python/bin/python3" -pip="${HOME}/python/bin/pip3" if ([ "${ImageOS}" == "ubuntu20" ]) && [ "${HOSTTYPE}" == "x86_64" ]; then "${python}" -m pip install --upgrade patchelf-wrapper "${python}" -m pip install --upgrade staticx fi -cd $whereibelong +cd "$whereibelong" diff --git a/.github/actions/linux-install.sh b/.github/actions/linux-install.sh index 921f8dd1..fb14ba22 100755 --- a/.github/actions/linux-install.sh +++ b/.github/actions/linux-install.sh @@ -6,14 +6,14 @@ rm -rf $gampath $pip install wheel $python -OO -m PyInstaller --clean --noupx --strip --distpath $gampath gam.spec export gam="${gampath}/gam" -export GAMVERSION=`$gam version simple` +export GAMVERSION=$($gam version simple) cp LICENSE $gampath cp GamCommands.txt $gampath this_glibc_ver=$(ldd --version | awk '/ldd/{print $NF}') GAM_ARCHIVE="gam-${GAMVERSION}-${GAMOS}-${PLATFORM}-glibc${this_glibc_ver}.tar.xz" rm $gampath/lastupdatecheck.txt # tar will cd to dist and tar up gam/ -tar -C ${distpath} --create --file $GAM_ARCHIVE --xz gam +tar -C ${distpath} --create --file "$GAM_ARCHIVE" --xz gam echo "PyInstaller GAM info:" du -h $gam time $gam version extended @@ -25,7 +25,7 @@ if ([ "${ImageOS}" == "ubuntu20" ]) && [ "${HOSTTYPE}" == "x86_64" ]; then mv $gam-staticx $gam chmod 755 $gam rm $gampath/lastupdatecheck.txt - tar -C dist/ --create --file $GAM_LEGACY_ARCHIVE --xz gam + tar -C dist/ --create --file "$GAM_LEGACY_ARCHIVE" --xz gam echo "Legacy StaticX GAM info:" du -h $gam time $gam version extended diff --git a/.github/actions/macos-before-install.sh b/.github/actions/macos-before-install.sh index 86c4d7dc..04182a75 100755 --- a/.github/actions/macos-before-install.sh +++ b/.github/actions/macos-before-install.sh @@ -24,9 +24,9 @@ cd ~ # with older MacOS versions export pyfile=python-$BUILD_PYTHON_VERSION-macos11.pkg -wget https://www.python.org/ftp/python/$BUILD_PYTHON_VERSION/$pyfile +wget https://www.python.org/ftp/python/"$BUILD_PYTHON_VERSION"/"$pyfile" echo "installing Python $BUILD_PYTHON_VERSION..." -sudo installer -pkg ./$pyfile -target / +sudo installer -pkg ./"$pyfile" -target / # This fixes https://github.com/pyinstaller/pyinstaller/issues/5062 #codesign --remove-signature /Library/Frameworks/Python.framework/Versions/3.10/Python @@ -128,4 +128,4 @@ $pip install pyscard $python -V -cd $whereibelong +cd "$whereibelong" diff --git a/.github/actions/macos-install.sh b/.github/actions/macos-install.sh index fe050fce..167d1c82 100755 --- a/.github/actions/macos-install.sh +++ b/.github/actions/macos-install.sh @@ -1,6 +1,6 @@ echo "MacOS Version Info According to Python:" macver=$(python -c "import platform; print(platform.mac_ver()[0])") -echo $macver +echo "$macver" echo "Xcode version:" xcodebuild -version export distpath="dist/" @@ -10,10 +10,10 @@ export specfile="gam.spec" $python -OO -m PyInstaller --distpath "${gampath}" "${specfile}" export gam="${gampath}/gam" $gam version extended -export GAMVERSION=`$gam version simple` +export GAMVERSION=$($gam version simple) cp LICENSE "${gampath}" cp GamCommands.txt "${gampath}" GAM_ARCHIVE="gam-${GAMVERSION}-${GAMOS}-${PLATFORM}.tar.xz" rm "${gampath}/lastupdatecheck.txt" # tar will cd to dist/ and tar up gam/ -tar -C dist/ --create --file $GAM_ARCHIVE --xz gam +tar -C dist/ --create --file "$GAM_ARCHIVE" --xz gam diff --git a/.github/actions/windows-before-install.sh b/.github/actions/windows-before-install.sh index 5e48824c..a34ba034 100755 --- a/.github/actions/windows-before-install.sh +++ b/.github/actions/windows-before-install.sh @@ -48,10 +48,10 @@ $pip install pyscard # cp -v /c/python/DLLs/libcrypto-1_1-x64.dll /c/python/DLLs/libcrypto-1_1.dll #fi -cd $mypath +cd "$mypath" echo "PATH: $PATH" cd .. $python setup.py install echo "cd to $mypath" -cd $mypath +cd "$mypath" diff --git a/.github/actions/windows-install.sh b/.github/actions/windows-install.sh index 4620bcd1..96c07cf7 100755 --- a/.github/actions/windows-install.sh +++ b/.github/actions/windows-install.sh @@ -19,13 +19,13 @@ cp gam-setup.bat $gampath GAM_ARCHIVE=gam-$GAMVERSION-$GAMOS-$PLATFORM.zip cwd=$(pwd) cd "${distpath}" -/c/Program\ Files/7-Zip/7z.exe a -tzip $GAM_ARCHIVE gam -xr!.svn +/c/Program\ Files/7-Zip/7z.exe a -tzip "$GAM_ARCHIVE" gam -xr!.svn mv "${GAM_ARCHIVE}" "${cwd}" cd "${cwd}" echo "Running WIX candle $WIX_BITS..." /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/candle.exe -arch $WIX_BITS gam.wxs echo "Done with WIX candle..." echo "Running WIX light..." -/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/light.exe -ext /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/WixUIExtension.dll gam.wixobj -o gam-$GAMVERSION-$GAMOS-$PLATFORM.msi || true; +/c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/light.exe -ext /c/Program\ Files\ \(x86\)/WiX\ Toolset\ v3.11/bin/WixUIExtension.dll gam.wixobj -o gam-"$GAMVERSION"-"$GAMOS"-"$PLATFORM".msi || true; echo "Done with WIX light..." rm *.wixpdb diff --git a/src/GamCommands.txt b/src/GamCommands.txt index 9ca99eae..dc5bdb11 100644 --- a/src/GamCommands.txt +++ b/src/GamCommands.txt @@ -655,7 +655,7 @@ Specify a collection of ChromeOS devices by directly specifying them ## Collections of Users -Specify a collection of Users by directly specifying them or by specifiying items that will yield a list of users +Specify a collection of Users by directly specifying them or by specifying items that will yield a list of users ::= (all users)| diff --git a/src/LICENSE b/src/LICENSE index b0de6145..0ef5c65b 100644 --- a/src/LICENSE +++ b/src/LICENSE @@ -501,7 +501,7 @@ This program is Copyright (C) Zeus Technology Limited 1996. This program may be used and copied freely providing this copyright notice is not removed. -This software is provided "as is" and any express or implied waranties, +This software is provided "as is" and any express or implied warranties, including but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, diff --git a/src/gam-install.sh b/src/gam-install.sh index 3fca5b62..b99c916a 100755 --- a/src/gam-install.sh +++ b/src/gam-install.sh @@ -52,7 +52,7 @@ done target_dir=${target_dir%/} update_profile() { - [ $2 -eq 1 ] || [ -f "$1" ] || return 1 + [ "$2" -eq 1 ] || [ -f "$1" ] || return 1 grep -F "$alias_line" "$1" > /dev/null 2>&1 if [ $? -ne 0 ]; then @@ -106,7 +106,7 @@ case $gamos in echo "This Linux distribution uses glibc $this_glibc_ver" useglibc="legacy" for gam_glibc_ver in $gam_glibc_vers; do - if version_gt $this_glibc_ver $gam_glibc_ver; then + if version_gt "$this_glibc_ver" "$gam_glibc_ver"; then useglibc="glibc$gam_glibc_ver" echo_green "Using GAM compiled against $useglibc" break @@ -137,7 +137,7 @@ case $gamos in gamfile="-windows-x86_64.zip" ;; *) - echo_red "Sorry, this installer currently only supports Linux and MacOS. Looks like you're runnning on $gamos. Exiting." + echo_red "Sorry, this installer currently only supports Linux and MacOS. Looks like you're running on $gamos. Exiting." exit ;; esac @@ -155,7 +155,7 @@ else fi echo_yellow "Checking GitHub URL $release_url for $gamversion GAM release ($check_type)..." -release_json=$(curl -s $GHCLIENT $release_url 2>&1 /dev/null) +release_json=$(curl -s "$GHCLIENT" "$release_url" 2>&1 /dev/null) echo_yellow "Getting file and download URL..." # Python is sadly the nearest to universal way to safely handle JSON with Bash @@ -205,12 +205,12 @@ if (( $rc != 0 )); then exit fi -browser_download_url=$(echo "$release_json" | $pycmd -c "$pycode" browser_download_url $gamversion) +browser_download_url=$(echo "$release_json" | $pycmd -c "$pycode" browser_download_url "$gamversion") if [[ ${browser_download_url:0:5} = "ERROR" ]]; then echo_red "${browser_download_url}" exit fi -name=$(echo "$release_json" | $pycmd -c "$pycode" name $gamversion) +name=$(echo "$release_json" | $pycmd -c "$pycode" name "$gamversion") if [[ ${name:0:5} = "ERROR" ]]; then echo_red "${name}" exit @@ -224,13 +224,13 @@ trap "rm -rf $temp_archive_dir" EXIT echo_yellow "Downloading file $name from $browser_download_url to $temp_archive_dir ($check_type)..." # Save archive to temp w/o losing our path -(cd $temp_archive_dir && curl -O -L $GHCLIENT $browser_download_url) +(cd "$temp_archive_dir" && curl -O -L "$GHCLIENT" "$browser_download_url") mkdir -p "$target_dir" echo_yellow "Extracting archive to $target_dir" if [[ "${name}" == *.tar.xz ]]; then - tar xf $temp_archive_dir/$name -C "$target_dir" + tar xf "$temp_archive_dir"/"$name" -C "$target_dir" else unzip "${temp_archive_dir}/${name}" -d "${target_dir}" fi @@ -294,7 +294,7 @@ while true; do if [ "$adminuser" == "" ]; then read -p "Please enter your Google Workspace admin email address: " adminuser fi - "$target_dir/gam/gam" create project $adminuser + "$target_dir/gam/gam" create project "$adminuser" rc=$? if (( $rc == 0 )); then echo_green "Project creation complete." @@ -319,7 +319,7 @@ while $project_created; do read -p "Are you ready to authorize GAM to perform Google Workspace management operations as your admin account? (yes or no) " yn case $yn in [Yy]*) - "$target_dir/gam/gam" oauth create $adminuser + "$target_dir/gam/gam" oauth create "$adminuser" rc=$? if (( $rc == 0 )); then echo_green "Admin authorization complete." @@ -348,7 +348,7 @@ while $project_created; do read -p "Please enter the email address of a regular Google Workspace user: " regularuser fi echo_yellow "Great! Checking service account scopes.This will fail the first time. Follow the steps to authorize and retry. It can take a few minutes for scopes to PASS after they've been authorized in the admin console." - "$target_dir/gam/gam" user $adminuser check serviceaccount + "$target_dir/gam/gam" user "$adminuser" check serviceaccount rc=$? if (( $rc == 0 )); then echo_green "Service account authorization complete." diff --git a/src/gam/__init__.py b/src/gam/__init__.py index 64ba2155..6d601994 100755 --- a/src/gam/__init__.py +++ b/src/gam/__init__.py @@ -4123,8 +4123,7 @@ def downloadDriveFile(users): while not done: status, done = downloader.next_chunk() if showProgress: - print('Downloaded: {:>7.2%}'.format( - status.progress())) + print(f'Downloaded: {status.progress():>7.2%}') else: _, content = drive._http.request(uri=spreadsheetUrl, method='GET') @@ -11235,7 +11234,7 @@ def run_batch(items): # Otherwise, the argument is preserved as is # # SubFields is a dictionary; the key is the argument number, the value is a list of tuples that mark -# the substition (fieldname, start, end). +# the substitution (fieldname, start, end). # Example: update user '~User' address type work unstructured '~~Street~~, ~~City~~, ~~State~~ ~~ZIP~~' primary # {2: [('User', 0, 5)], 7: [('Street', 0, 10), ('City', 12, 20), ('State', 22, 31), ('ZIP', 32, 39)]} # diff --git a/src/gam/auth/oauth_test.py b/src/gam/auth/oauth_test.py index 7b8e4ee2..74910b44 100644 --- a/src/gam/auth/oauth_test.py +++ b/src/gam/auth/oauth_test.py @@ -44,8 +44,8 @@ class CredentialsTest(unittest.TestCase): # Remove any credential files that may have been created. if os.path.exists(self.fake_filename): os.remove(self.fake_filename) - if os.path.exists('%s.lock' % self.fake_filename): - os.remove('%s.lock' % self.fake_filename) + if os.path.exists(f'{self.fake_filename}.lock'): + os.remove(f'{self.fake_filename}.lock') super().tearDown() def test_from_authorized_user_info_only_required_info(self): @@ -126,7 +126,7 @@ class CredentialsTest(unittest.TestCase): client_secret=self.fake_client_secret, filename=self.fake_filename) self.assertIsInstance(creds._lock, oauth.FileLock) - self.assertEqual(creds._lock.lock_file, '%s.lock' % creds.filename) + self.assertEqual(creds._lock.lock_file, f'{creds.filename}.lock') def test_credentials_uses_thread_lock_when_filename_not_provided(self): creds = oauth.Credentials(token=self.fake_token, @@ -540,7 +540,7 @@ class CredentialsTest(unittest.TestCase): client_id=self.fake_client_id, client_secret=self.fake_client_secret, filename=self.fake_filename) - lock_file = '%s.lock' % creds.filename + lock_file = f'{creds.filename}.lock' creds.write() self.assertTrue(os.path.exists(lock_file)) creds.delete() @@ -564,9 +564,9 @@ class CredentialsTest(unittest.TestCase): creds.revoke(http=mock_http) uri = mock_http.request.call_args[0][0] - self.assertRegex(uri, '^%s' % oauth.Credentials._REVOKE_TOKEN_BASE_URI) + self.assertRegex(uri, f'^{oauth.Credentials._REVOKE_TOKEN_BASE_URI}') params = uri[uri.index('?'):] - self.assertIn('token=%s' % creds.refresh_token, params) + self.assertIn(f'token={creds.refresh_token}', params) self.assertEqual('GET', mock_http.request.call_args[0][1]) diff --git a/src/gam/controlflow_test.py b/src/gam/controlflow_test.py index eb4aa29b..a803164f 100644 --- a/src/gam/controlflow_test.py +++ b/src/gam/controlflow_test.py @@ -79,7 +79,7 @@ class ControlFlowTest(unittest.TestCase): controlflow.wait_on_failure( attempt, total_attempts, - 'Attempt #%s' % attempt, + f'Attempt #{attempt}', # Suppress messages while we make a lot of attempts. error_print_threshold=total_attempts + 1) # Wait time may be between 60 and 61 secs, due to rand addition. @@ -102,7 +102,7 @@ class ControlFlowTest(unittest.TestCase): for attempt in range(1, total_attempts + 1): controlflow.wait_on_failure(attempt, total_attempts, - 'Attempt #%s' % attempt, + f'Attempt #{attempt}', error_print_threshold=threshold) self.assertEqual(total_attempts - threshold, mock_stderr_write.call_count) diff --git a/src/gam/gapi/errors.py b/src/gam/gapi/errors.py index efff746f..038751da 100644 --- a/src/gam/gapi/errors.py +++ b/src/gam/gapi/errors.py @@ -1,10 +1,9 @@ """GAPI and OAuth Token related errors methods.""" -from enum import Enum import json +from enum import Enum -from gam import controlflow -from gam import display +from gam import controlflow, display from gam.var import UTF8 @@ -282,7 +281,7 @@ def get_gapi_error_detail(e, Args: e: googleapiclient.HttpError, The HTTP Error received. - soft_errors: Boolean, If true, causes error messages to be surpressed, + soft_errors: Boolean, If true, causes error messages to be suppressed, rather than sending them to stderr. silent_errors: Boolean, If true, suppresses and ignores any errors from being displayed diff --git a/src/gam/gapi/vault.py b/src/gam/gapi/vault.py index e2ac331e..de94eb04 100644 --- a/src/gam/gapi/vault.py +++ b/src/gam/gapi/vault.py @@ -822,8 +822,7 @@ def downloadExport(): done = False while not done: status, done = downloader.next_chunk() - sys.stdout.write(' Downloaded: {:>7.2%}\r'.format( - status.progress())) + sys.stdout.write(f' Downloaded: {status.progress():>7.2%}\r') sys.stdout.flush() sys.stdout.write('\n Download complete. Flushing to disk...\n') fileutils.close_file(f, True) diff --git a/src/gam/utils.py b/src/gam/utils.py index f84f1a95..d080258b 100644 --- a/src/gam/utils.py +++ b/src/gam/utils.py @@ -119,7 +119,7 @@ def dehtml(text): def indentMultiLineText(message, n=0): - return message.replace('\n', '\n{}'.format(' ' * n)).rstrip() + return message.replace('\n', f"\n{' ' * n}").rstrip() def flatten_json(structure, key='', path='', flattened=None, listLimit=None): diff --git a/src/versionhistory-v1.json b/src/versionhistory-v1.json index 6b74eb55..c0f2a686 100644 --- a/src/versionhistory-v1.json +++ b/src/versionhistory-v1.json @@ -99,7 +99,7 @@ "$ref": "ListPlatformsResponse" }, "path": "v1/{+parent}/platforms", - "description": "Returns list of platforms that are avaialble for a given product. The resource \"product\" has no resource name in its name.", + "description": "Returns list of platforms that are available for a given product. The resource \"product\" has no resource name in its name.", "flatPath": "v1/{v1Id}/platforms", "id": "versionhistory.platforms.list", "parameters": { @@ -153,7 +153,7 @@ "filter": { "type": "string", "location": "query", - "description": "Optional. Filter string. Format is a comma separated list of All comma separated filter clauses are conjoined with a logical \"and\". Valid field_names are \"version\", \"name\", \"platform\", \"channel\", \"fraction\" \"starttime\", and \"endtime\". Valid operators are \"\u003c\", \"\u003c=\", \"=\", \"\u003e=\", and \"\u003e\". Channel comparison is done by distance from stable. must be a valid channel when filtering by channel. Ex) stable \u003c beta, beta \u003c dev, canary \u003c canary_asan. Version comparison is done numerically. Ex) 1.0.0.8 \u003c 1.0.0.10. If version is not entirely written, the version will be appended with 0 for the missing fields. Ex) version \u003e 80 becoms version \u003e 80.0.0.0 When filtering by starttime or endtime, string must be in RFC 3339 date string format. Name and platform are filtered by string comparison. Ex) \"...?filter=channel\u003c=beta, version \u003e= 80 Ex) \"...?filter=version \u003e 80, version \u003c 81 Ex) \"...?filter=starttime\u003e2020-01-01T00:00:00Z" + "description": "Optional. Filter string. Format is a comma separated list of All comma separated filter clauses are conjoined with a logical \"and\". Valid field_names are \"version\", \"name\", \"platform\", \"channel\", \"fraction\" \"starttime\", and \"endtime\". Valid operators are \"\u003c\", \"\u003c=\", \"=\", \"\u003e=\", and \"\u003e\". Channel comparison is done by distance from stable. must be a valid channel when filtering by channel. Ex) stable \u003c beta, beta \u003c dev, canary \u003c canary_asan. Version comparison is done numerically. Ex) 1.0.0.8 \u003c 1.0.0.10. If version is not entirely written, the version will be appended with 0 for the missing fields. Ex) version \u003e 80 becomes version \u003e 80.0.0.0 When filtering by starttime or endtime, string must be in RFC 3339 date string format. Name and platform are filtered by string comparison. Ex) \"...?filter=channel\u003c=beta, version \u003e= 80 Ex) \"...?filter=version \u003e 80, version \u003c 81 Ex) \"...?filter=starttime\u003e2020-01-01T00:00:00Z" }, "orderBy": { "location": "query", @@ -208,7 +208,7 @@ "description": "Optional. Ordering string. Valid order_by strings are \"version\", \"name\", \"platform\", and \"channel\". Optionally, you can append \" desc\" or \" asc\" to specify the sorting order. Multiple order_by strings can be used in a comma separated list. Ordering by channel will sort by distance from the stable channel (not alphabetically). A list of channels sorted in this order is: stable, beta, dev, canary, and canary_asan. Sorting by name may cause unexpected behaviour as it is a naive string sort. For example, 1.0.0.8 will be before 1.0.0.10 in descending order. If order_by is not specified the response will be sorted by version in descending order. Ex) \"...?order_by=version asc\" Ex) \"...?order_by=platform desc, channel, version\"" }, "filter": { - "description": "Optional. Filter string. Format is a comma separated list of All comma separated filter clauses are conjoined with a logical \"and\". Valid field_names are \"version\", \"name\", \"platform\", and \"channel\". Valid operators are \"\u003c\", \"\u003c=\", \"=\", \"\u003e=\", and \"\u003e\". Channel comparison is done by distance from stable. Ex) stable \u003c beta, beta \u003c dev, canary \u003c canary_asan. Version comparison is done numerically. If version is not entirely written, the version will be appended with 0 in missing fields. Ex) version \u003e 80 becoms version \u003e 80.0.0.0 Name and platform are filtered by string comparison. Ex) \"...?filter=channel\u003c=beta, version \u003e= 80 Ex) \"...?filter=version \u003e 80, version \u003c 81", + "description": "Optional. Filter string. Format is a comma separated list of All comma separated filter clauses are conjoined with a logical \"and\". Valid field_names are \"version\", \"name\", \"platform\", and \"channel\". Valid operators are \"\u003c\", \"\u003c=\", \"=\", \"\u003e=\", and \"\u003e\". Channel comparison is done by distance from stable. Ex) stable \u003c beta, beta \u003c dev, canary \u003c canary_asan. Version comparison is done numerically. If version is not entirely written, the version will be appended with 0 in missing fields. Ex) version \u003e 80 becomes version \u003e 80.0.0.0 Name and platform are filtered by string comparison. Ex) \"...?filter=channel\u003c=beta, version \u003e= 80 Ex) \"...?filter=version \u003e 80, version \u003c 81", "location": "query", "type": "string" }