зеркало из https://github.com/mozilla/ff-tool.git
Address some PR feedback, update README
This commit is contained in:
Родитель
1f6a8d9b52
Коммит
cfd2a26aee
88
README.md
88
README.md
|
@ -1,29 +1,28 @@
|
|||
# ff-tool
|
||||
|
||||
**Summary**
|
||||
## Summary
|
||||
|
||||
ff-tool is a Python CLI tool we've created to facilitate browser testing of
|
||||
cloud services. It is largely a convenience wrapper we've written around
|
||||
these amazing tools/libraries (see note below):
|
||||
ff-tool is a Python CLI tool we've created to facilitate browser testing of
|
||||
cloud services. It is largely a convenience wrapper we've written around
|
||||
these amazing tools/libraries (see note below):
|
||||
|
||||
* [mozdownload](https://github.com/mozilla/mozdownload)
|
||||
* [mozprofile](https://github.com/mozilla/mozprofile)
|
||||
- [mozdownload](https://github.com/mozilla/mozdownload)
|
||||
- [mozprofile](https://github.com/mozilla/mozprofile)
|
||||
|
||||
Our typical use case is launching various Firefox browser versions with a
|
||||
Our typical use case is launching various Firefox browser versions with a
|
||||
fresh profile and loading custom preferences. This tool enables us to do this
|
||||
quickly with a 1-liner from the CLI.
|
||||
|
||||
**Features**
|
||||
## Features
|
||||
|
||||
1. DownloadFirefox desktop versions
|
||||
** (Nightly, Developer Edition, Beta, Release)
|
||||
2. Manage profiles
|
||||
3. Load test preferences
|
||||
1. DownloadFirefox desktop versions (Nightly, Developer Edition, Beta, Release)
|
||||
2. Manage profiles
|
||||
3. Load test preferences
|
||||
|
||||
**Notes**
|
||||
## Notes
|
||||
|
||||
If you plan on creating a tool of your own, please import the above libs
|
||||
directly in your script(s). This tool was designed for convenience of our
|
||||
If you plan on creating a tool of your own, please import the above lib
|
||||
directly in your script(s). This tool was designed for convenience of our
|
||||
team for testing Cloud Services and not intended to be used as a library.
|
||||
|
||||
Profiles are stored in a temp directory by default which can be overridden.
|
||||
|
@ -31,31 +30,32 @@ Use caution if you specify your own profile directory as profile cleanup
|
|||
functions can wipe out all profiles in your specified directory.
|
||||
|
||||
|
||||
:bangbang: _NOTE: This tool is work in progress... DO NOT USE_ :bangbang:
|
||||
:bangbang: _**NOTE:** This tool is work in progress... DO NOT USE_ :bangbang:
|
||||
|
||||
|
||||
# Installation
|
||||
## Installation
|
||||
|
||||
## Pre-requisites
|
||||
* ff-tool requires you have python and virtualenv installed.
|
||||
### Pre-requisites
|
||||
|
||||
## Build
|
||||
```
|
||||
**NOTE:** ff-tool requires you have Python and virtualenv installed
|
||||
|
||||
### Build
|
||||
```sh
|
||||
$ make build
|
||||
$ source ./venv/bin/activate
|
||||
```
|
||||
|
||||
## Cleanup
|
||||
```
|
||||
### Cleanup
|
||||
```sh
|
||||
$ deactivate
|
||||
$ make clean
|
||||
$ make clean
|
||||
```
|
||||
|
||||
# Run
|
||||
## Run
|
||||
_When not specified, ff will use defaults_
|
||||
|
||||
## Help
|
||||
```
|
||||
## Help
|
||||
```sh
|
||||
$ ff -h
|
||||
```
|
||||
|
||||
|
@ -63,14 +63,14 @@ $ ff -h
|
|||
|
||||
* version: Nightly
|
||||
* profile_name: \<random\>
|
||||
```
|
||||
$ ff run
|
||||
```sh
|
||||
$ ff
|
||||
```
|
||||
|
||||
* version: Developer Edition (aurora)
|
||||
* version: Developer Edition (aurora)
|
||||
* profile_name: \<random\>
|
||||
```
|
||||
$ ff run -c aurora
|
||||
```sh
|
||||
$ ff -c aurora
|
||||
```
|
||||
|
||||
## Launch browser, clean profile, specify profile name
|
||||
|
@ -78,30 +78,38 @@ $ ff run -c aurora
|
|||
* version: Nightly
|
||||
* profile_name: my_cool_profile1
|
||||
|
||||
NOTE: if profile exists, we use it, if not we create a new one
|
||||
**NOTE:** If the specified profile exists, we use it, if not we create a new one
|
||||
with that name.
|
||||
|
||||
```
|
||||
$ ff run -p my_cool_profile1
|
||||
```sh
|
||||
$ ff -p my_cool_profile1
|
||||
```
|
||||
|
||||
# Cloud Services (only)
|
||||
## Launch browser, clean profile, specify services-specific options...
|
||||
## Launch browser, clean profile, specify services-specific options...
|
||||
|
||||
* version: Beta
|
||||
* version: Beta
|
||||
* profile_name: my_cool_profile1
|
||||
* product: loop-server
|
||||
* environment: stage
|
||||
* test-type: e2e-test
|
||||
|
||||
NOTE: if profile exists, we use it, if not we create a new one
|
||||
**NOTE:** If the specified profile exists, we use it, if not we create a new one
|
||||
with that name.
|
||||
|
||||
```
|
||||
$ ff run -c aurora -p my_cool_profile1 -a loop-server -e stage -t e2e-test
|
||||
```sh
|
||||
$ ff -c beta -p my_cool_profile1 -a loop-server -e stage -t e2e-test
|
||||
```
|
||||
|
||||
## Download all browsers, but don't create a profile or launch any browsers...
|
||||
|
||||
**NOTE:** This is useful for our daily refresh task where we make sure we have
|
||||
the latest browsers installed.
|
||||
|
||||
* version: all
|
||||
* profile_name: none
|
||||
|
||||
```sh
|
||||
$ ff -c ALL --no-profile --no-launch
|
||||
```
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
[DEFAULT]
|
||||
PATH_FIREFOX_APP = _temp/browsers/%(APP_DEST_FILENAME)s
|
||||
PATH_FIREFOX_BIN = Contents/MacOS/firefox
|
||||
PATH_FIREFOX_BIN_ENV = %(PATH_FIREFOX_APP)s/%(PATH_FIREFOX_BIN)s
|
||||
PATH_FIREFOX_BIN_ENV = %(PATH_FIREFOX_APP)s/Contents/MacOS/firefox
|
||||
PATH_FIREFOX_PROFILES_ENV = $HOME/Library/Application Support/Firefox/
|
||||
|
||||
[nightly]
|
||||
|
|
|
@ -9,9 +9,10 @@ Module to download OS-specific versions of Firefox:
|
|||
import os
|
||||
import time
|
||||
|
||||
from firefox_install import install
|
||||
from firefox_install import install, get_firefox_version
|
||||
from firefox_env_handler import IniHandler
|
||||
from mozdownload import FactoryScraper
|
||||
from outlawg import Outlawg
|
||||
|
||||
try:
|
||||
import configparser # Python 3
|
||||
|
@ -21,6 +22,7 @@ except:
|
|||
|
||||
BASE_DIR = os.path.join('_temp', 'browsers')
|
||||
CONFIG_CHANNELS = os.path.join('configs', 'channels.ini')
|
||||
SCRIPT_START_TIME = time.time()
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read(CONFIG_CHANNELS)
|
||||
|
@ -28,7 +30,7 @@ config.read(CONFIG_CHANNELS)
|
|||
env = IniHandler()
|
||||
env.load_os_config('configs')
|
||||
|
||||
SCRIPT_START_TIME = time.time()
|
||||
out = Outlawg()
|
||||
|
||||
|
||||
def replace_ext(filename, ext):
|
||||
|
@ -62,16 +64,23 @@ def download(channel):
|
|||
destination=download_path
|
||||
)
|
||||
|
||||
print("Downloading {0} to {1}".format(channel, download_path))
|
||||
args = {"channel": channel, "download_path": download_path}
|
||||
print("Downloading {channel} to {download_path}".format(**args))
|
||||
scraper.download()
|
||||
|
||||
is_recent_file = modification_date(download_path) > SCRIPT_START_TIME
|
||||
firefox_bin = env.get(channel, 'PATH_FIREFOX_BIN_ENV')
|
||||
|
||||
if is_recent_file:
|
||||
print("You should install")
|
||||
# If the *.dmg file was downloaded recently, or we don't have the *.app
|
||||
# file installed, install the current Firefox channel.
|
||||
if is_recent_file or not os.path.exists(firefox_bin):
|
||||
install(channel)
|
||||
|
||||
else:
|
||||
print("Not recent. skipping install")
|
||||
firefox_version = get_firefox_version(channel)
|
||||
args = {"channel": channel, "version": firefox_version}
|
||||
msg = "You have the latest version of {channel} installed ({version})."
|
||||
out.header(msg.format(**args))
|
||||
|
||||
|
||||
def download_all():
|
||||
|
|
|
@ -7,6 +7,10 @@ import shutil
|
|||
import sys
|
||||
import configparser
|
||||
|
||||
from outlawg import Outlawg
|
||||
|
||||
out = Outlawg()
|
||||
|
||||
|
||||
class FirefoxEnvHandler():
|
||||
LINUX = 'linux'
|
||||
|
@ -52,19 +56,6 @@ class FirefoxEnvHandler():
|
|||
"""
|
||||
return cls.get_os() == cls.WINDOWS
|
||||
|
||||
@staticmethod
|
||||
def banner(str, length=79, delimiter='='):
|
||||
"""
|
||||
Throws up a debug header to make output subjectively "easier" to read
|
||||
in stdout.
|
||||
"""
|
||||
if length <= 0:
|
||||
length = len(str)
|
||||
|
||||
divider = delimiter * length
|
||||
output = '\n'.join(['', divider, str, divider])
|
||||
print(output)
|
||||
|
||||
@staticmethod
|
||||
def clean_folder(path, foot_gun=True):
|
||||
"""
|
||||
|
@ -93,7 +84,7 @@ class IniHandler(FirefoxEnvHandler):
|
|||
"""
|
||||
Load an INI config based on the specified `ini_path`.
|
||||
"""
|
||||
IniHandler.banner('LOADING {0}'.format(ini_path), 79, '-')
|
||||
out.header('LOADING {0}'.format(ini_path))
|
||||
|
||||
# Make sure the specified config file exists, fail hard if missing.
|
||||
if not os.path.isfile(ini_path):
|
||||
|
@ -115,7 +106,7 @@ class IniHandler(FirefoxEnvHandler):
|
|||
Generate and save the output environment file so we can source it from
|
||||
something like .bashrc or .bashprofile.
|
||||
"""
|
||||
IniHandler.banner('CREATING ENV FILE ({0})'.format(out_file))
|
||||
out.header('CREATING ENV FILE ({0})'.format(out_file))
|
||||
|
||||
env_fmt = "export %s=\"%s\""
|
||||
env_vars = []
|
||||
|
|
|
@ -3,18 +3,20 @@
|
|||
import os
|
||||
from firefox_env_handler import IniHandler
|
||||
from fabric.api import local
|
||||
|
||||
from outlawg import Outlawg
|
||||
|
||||
env = IniHandler()
|
||||
env.load_os_config('configs')
|
||||
|
||||
out = Outlawg()
|
||||
|
||||
|
||||
def install(channel):
|
||||
if channel == 'ALL':
|
||||
install_all()
|
||||
return
|
||||
|
||||
filename = env.get(channel, 'DOWNLOAD_FILENAME')
|
||||
# filename = env.get(channel, 'DOWNLOAD_FILENAME')
|
||||
install_dir = env.get(channel, 'PATH_FIREFOX_APP')
|
||||
|
||||
if IniHandler.is_linux():
|
||||
|
@ -40,6 +42,15 @@ def install(channel):
|
|||
|
||||
extract_dmg(dmg_dirname, app_src_filename, app_dest_filename, channel)
|
||||
|
||||
firefox_version = get_firefox_version(channel)
|
||||
out.header("Installed {0} ({1})".format(firefox_version, channel))
|
||||
|
||||
|
||||
def get_firefox_version(channel):
|
||||
path_firefox_bin = env.get(channel, "PATH_FIREFOX_BIN_ENV")
|
||||
cmd = "{0} --version".format(path_firefox_bin)
|
||||
return local(cmd, capture=True)
|
||||
|
||||
|
||||
def install_all():
|
||||
for channel in env.sections():
|
||||
|
|
|
@ -21,7 +21,7 @@ except:
|
|||
import ConfigParser as configparser # Python 2
|
||||
|
||||
PATH_PROJECT = os.path.abspath('.')
|
||||
PROFILE_DIR = os.path.join(PATH_PROJECT, '_temp', 'profiles')
|
||||
BASE_PROFILE_DIR = os.path.join(PATH_PROJECT, '_temp', 'profiles')
|
||||
FILE_PREFS = 'prefs.ini'
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
|
@ -41,6 +41,8 @@ def prefs_paths(application, test_type, env='stage'):
|
|||
# Make sure the specified INI file has the specified section.
|
||||
if config.has_section(env):
|
||||
valid_paths.append(path_app + ":" + env)
|
||||
else:
|
||||
valid_paths.append(path_app)
|
||||
|
||||
if test_type:
|
||||
path_app_test_type = os.path.join(path_app_dir, test_type, FILE_PREFS)
|
||||
|
@ -48,23 +50,22 @@ def prefs_paths(application, test_type, env='stage'):
|
|||
config.read(path_app_test_type)
|
||||
if config.has_section(env):
|
||||
valid_paths.append(path_app_test_type + ":" + env)
|
||||
else:
|
||||
valid_paths.append(path_app_test_type)
|
||||
|
||||
return valid_paths
|
||||
|
||||
|
||||
def create_mozprofile(profile_dir, application=None, test_type=None, env=None):
|
||||
if not profile_dir:
|
||||
print("Create random profile")
|
||||
full_profile_dir = mkdtemp(dir=PROFILE_DIR, prefix="fftool.", suffix="")
|
||||
print(full_profile_dir)
|
||||
else:
|
||||
print("Using named profile")
|
||||
full_profile_dir = os.path.join(PROFILE_DIR, profile_dir)
|
||||
full_profile_dir = mkdtemp(dir=BASE_PROFILE_DIR, prefix="fftool.", suffix="")
|
||||
|
||||
# If temp profile already exists, kill it so it doesn't merge unexpectedly.
|
||||
if os.path.exists(full_profile_dir):
|
||||
msg = "WARNING: Profile '{0}' already exists. Merging configs."
|
||||
out.header(msg.format(profile_dir), 'XL', '-')
|
||||
else:
|
||||
full_profile_dir = os.path.join(BASE_PROFILE_DIR, profile_dir)
|
||||
|
||||
if os.path.exists(full_profile_dir):
|
||||
msg = "WARNING: Profile '{0}' already exists. Merging configs."
|
||||
out.header(msg.format(full_profile_dir), 'XL', '-')
|
||||
|
||||
prefs = Preferences()
|
||||
for path in prefs_paths(application, test_type):
|
||||
|
|
|
@ -2,18 +2,10 @@ import os
|
|||
|
||||
from firefox_env_handler import IniHandler
|
||||
|
||||
try:
|
||||
import configparser # Python 3
|
||||
except:
|
||||
import ConfigParser as configparser # Python 2
|
||||
|
||||
from fabric.api import local # It looks like Fabric may only support Python 2.
|
||||
|
||||
PATH_PROJECT = os.path.abspath('.')
|
||||
PATH_TEMP = os.path.join(PATH_PROJECT, '_temp', 'profiles')
|
||||
FILE_PREFS = 'prefs.ini'
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
env = IniHandler()
|
||||
env.load_os_config('configs')
|
||||
|
@ -21,16 +13,10 @@ env.load_os_config('configs')
|
|||
|
||||
def launch_firefox(profile_path, channel):
|
||||
"""
|
||||
this will be an all-encompassing function that may make use of all the
|
||||
others (with the exception of uninstall
|
||||
This function will rely on the other functions (download, install, profile)
|
||||
having successfully done their business.
|
||||
"""
|
||||
|
||||
# we need to invoke each of these here
|
||||
print("1. download firefox! (if not already)")
|
||||
print("2. install firefox! (if not already)")
|
||||
print("3. create profile! (unless we specify an existing)")
|
||||
print("4. Launch firefox!")
|
||||
|
||||
FIREFOX_APP_BIN = env.get(channel, 'PATH_FIREFOX_BIN_ENV')
|
||||
PROFILE_PATH = os.path.join(PATH_TEMP, profile_path)
|
||||
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
"""
|
||||
NOTE: THIS IS AN OSX SPECIFIC FILE, SPECIFICALLY FOR MOUNTING DMG FILES.
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
import shutil
|
||||
from fabric.api import local
|
||||
|
||||
|
||||
cmd_hdiutil = local("which hdiutil", capture=True)
|
||||
|
||||
|
||||
def replace_ext(filename, ext):
|
||||
"""
|
||||
Takes a filename, and changes it's extension.
|
||||
"""
|
||||
basename = os.path.splitext(filename)[0]
|
||||
args = {"basename": basename, "ext": ext}
|
||||
return "{basename}.{ext}".format(**args)
|
||||
|
||||
|
||||
def attach(dmg_path, mountpoint):
|
||||
args = {
|
||||
"hdiutil": cmd_hdiutil,
|
||||
|
@ -55,17 +50,3 @@ def extract_dmg(dmg_path, app_src_filename, app_dest_filename, channel):
|
|||
attach(dmg_path, mountpoint=tmp_dirname)
|
||||
move_app(app_src_path, app_dest_path)
|
||||
detach(mountpoint=tmp_dirname)
|
||||
ver = get_firefox_version(app_dest_path)
|
||||
print("Installed {0} ({1})".format(ver, channel))
|
||||
|
||||
|
||||
def get_firefox_version(app):
|
||||
bin = os.path.join(app, "Contents", "MacOS", "firefox")
|
||||
if not os.path.exists(bin):
|
||||
print("{0} not found. Aborting.".format(bin))
|
||||
return
|
||||
|
||||
cmd = "{0} --version".format(bin)
|
||||
output = local(cmd, capture=True)
|
||||
for line in output.splitlines():
|
||||
return line
|
||||
|
|
|
@ -26,7 +26,6 @@ def main():
|
|||
'--channel',
|
||||
choices=CHANNELS,
|
||||
default=DEFAULT_CHANNEL,
|
||||
type=str,
|
||||
help='A specific Firefox channel.'
|
||||
)
|
||||
|
||||
|
@ -34,7 +33,7 @@ def main():
|
|||
parser.add_argument(
|
||||
'-p',
|
||||
'--profile',
|
||||
help='Name of the Firefox to create/use.'
|
||||
help='Name of the Firefox profile to create/use.'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
|
@ -46,7 +45,7 @@ def main():
|
|||
parser.add_argument(
|
||||
'-t',
|
||||
'--test-type',
|
||||
help="TODO: (ie: e2e-test, stack-check)."
|
||||
help="Name of the test-type (ie: e2e-test, stack-check)."
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
|
@ -58,40 +57,33 @@ def main():
|
|||
parser.add_argument(
|
||||
'--no-launch',
|
||||
action='store_true',
|
||||
help="TODO:"
|
||||
help="Whether or not to launch a Firefox instance."
|
||||
)
|
||||
|
||||
"""
|
||||
TODO-this:
|
||||
parser.add_argument(
|
||||
'--no-profile',
|
||||
help="TODO:"
|
||||
action='store_true',
|
||||
help="Whether to create a profile. This is used for the daily refresh job."
|
||||
)
|
||||
"""
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
# INSTALL
|
||||
print("Installing...")
|
||||
# DOWNLOAD/INSTALL
|
||||
download(options.channel)
|
||||
|
||||
# PROFILE
|
||||
print("Creating profile...")
|
||||
|
||||
profile_path = create_mozprofile(options.profile, options.app, options.test_type, options.env)
|
||||
if not options.no_profile:
|
||||
profile_path = create_mozprofile(
|
||||
options.profile,
|
||||
application=options.app,
|
||||
test_type=options.test_type,
|
||||
env=options.env
|
||||
)
|
||||
|
||||
# LAUNCH
|
||||
if not options.no_launch:
|
||||
print("Launching!")
|
||||
launch_firefox(profile_path, channel=options.channel)
|
||||
|
||||
"""
|
||||
if not options.env:
|
||||
print("Unknown env")
|
||||
else:
|
||||
print("Load settings for env: {0}".format(options.env))
|
||||
"""
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
Fabric3
|
||||
configargparse
|
||||
configparser
|
||||
mozdownload==1.20.2
|
||||
mozprofile
|
||||
mozprofile==0.28
|
||||
tox==2.3.1
|
||||
flake8==2.5.4
|
||||
outlawg
|
||||
|
|
Загрузка…
Ссылка в новой задаче