Skip to content

Commit 0ca65a8

Browse files
authored
Merge pull request #651 from screamerbg/feature_offline_mode
New feature: Offline mode
2 parents d6649ea + 98a2e61 commit 0ca65a8

File tree

3 files changed

+60
-20
lines changed

3 files changed

+60
-20
lines changed

README.md

+6
Original file line numberDiff line numberDiff line change
@@ -879,6 +879,12 @@ For safety reasons, Mbed CLI uses the `mbed-cache` subfolder to a user specified
879879

880880
**Security notice**: If you use cache location outside your user home/profile directory, then other system users might be able to access the repository cache and therefore the data of the cached repositories.
881881

882+
### Offline mode
883+
884+
Through the caching feature in Mbed CLI, you can enable offline mode, which uses the already cached repositories on your system. You can enable offline mode by adding the `--offline` switch to `mbed import`, `mbed add`, `mbed update` and `mbed new`.
885+
886+
In offline mode, Mbed CLI looks up locally cached repositories and uses them without fetching new content from their remote repositories. This is particularly useful if for example you are in a plane and you'd like to create another Mbed OS project (assuming you've imported or created one before), but you don't have access to the internet. By using the command `mbed new <project_name> --offline`, you can create the project with Mbed OS included.
887+
882888
## Mbed CLI configuration
883889

884890
You can streamline many options in Mbed CLI with global and local configuration.

circle.yml

+5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ test:
55
- mbed toolchain -G GCC_ARM
66
- mbed target -G K64F
77
- mbed config -G protocol ssh
8+
- mbed config -G --list
89
- cd .tests && mbed new new-test
910
- cd .tests/new-test && mbed ls
1011
- cd .tests/new-test && mbed releases -r
@@ -22,6 +23,10 @@ test:
2223
- cd .tests/bld-test/mbed && mbed update 85 --clean
2324
- cd .tests/bld-test && mbed update --clean
2425
- cd .tests/bld-test && mbed compile -m LPC1768 -j 0
26+
- cd .tests && mbed import https://github.com/ARMmbed/mbed-os-example-mesh-minimal offline-test
27+
- cd .tests/offline-test && mbed update mbed-os-5.5.6 --offline
28+
- cd .tests/offline-test && mbed update mbed-os-5.7.5 --offline
29+
- cd .tests && mbed import https://github.com/ARMmbed/mbed-os-example-mesh-minimal offline-test2 --offline
2530
- cd .tests && mbed new supported-tests
2631
- |-
2732
cd .tests/supported-tests

mbed/mbed.py

+49-20
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,15 @@ def error(msg, code=-1):
167167
sys.stderr.write("---\n")
168168
sys.exit(code)
169169

170+
def offline_warning(offline, top=True):
171+
if top and offline:
172+
log("\n")
173+
log(".=============================== OFFLINE MODE ================================.\n")
174+
log("| Offline mode is enabled. No connections to remote repositories will be |\n")
175+
log("| made and only locally cached repositories will be used. |\n")
176+
log("| This might break some actions if non-cached repositories are referenced. |\n")
177+
log("'============================================================================='\n\n")
178+
170179
def progress_cursor():
171180
while True:
172181
for cursor in '|/-\\':
@@ -1179,7 +1188,7 @@ def remove(self, dest, *args, **kwargs):
11791188
pass
11801189
return self.scm.remove(dest, *args, **kwargs)
11811190

1182-
def clone(self, url, path, rev=None, depth=None, protocol=None, **kwargs):
1191+
def clone(self, url, path, rev=None, depth=None, protocol=None, offline=False, **kwargs):
11831192
# Sorted so repositories that match urls are attempted first
11841193
info("Trying to guess source control management tool. Supported SCMs: %s" % ', '.join([s.name for s in scms.values()]))
11851194
for scm in scms.values():
@@ -1202,7 +1211,7 @@ def clone(self, url, path, rev=None, depth=None, protocol=None, **kwargs):
12021211
info("Update cached copy from remote repository")
12031212
if not rev:
12041213
rev = scm.default_branch
1205-
scm.update(rev, True)
1214+
scm.update(rev, True, is_local=offline)
12061215
main = False
12071216
except (ProcessException, IOError):
12081217
info("Discarding cached repository")
@@ -1211,6 +1220,8 @@ def clone(self, url, path, rev=None, depth=None, protocol=None, **kwargs):
12111220

12121221
# Main clone routine if the clone with cache ref failed (might occur if cache ref is dirty)
12131222
if main:
1223+
if offline:
1224+
continue
12141225
try:
12151226
scm.clone(url, path, depth=depth, protocol=protocol, **kwargs)
12161227
except ProcessException:
@@ -1225,6 +1236,11 @@ def clone(self, url, path, rev=None, depth=None, protocol=None, **kwargs):
12251236
self.set_cache(url)
12261237
return True
12271238

1239+
if offline:
1240+
error("Unable to clone repository \"%s\" in offline mode ('--offline' used)." % url)
1241+
if os.path.isdir(path):
1242+
rmtree_readonly(path)
1243+
12281244
return False
12291245

12301246
def getlibs(self):
@@ -1816,14 +1832,16 @@ def thunk(parsed_args):
18161832
dict(name='--create-only', action='store_true', help='Only create a program, do not import mbed-os or mbed library.'),
18171833
dict(name='--depth', nargs='?', help='Number of revisions to fetch the mbed OS repository when creating new program. Default: all revisions.'),
18181834
dict(name='--protocol', nargs='?', help='Transport protocol when fetching the mbed OS repository when creating new program. Supported: https, http, ssh, git. Default: inferred from URL.'),
1835+
dict(name='--offline', action='store_true', help='Offline mode will force the use of locally cached repositories and prevent requests to remote repositories.'),
18191836
help='Create new mbed program or library',
18201837
description=(
18211838
"Creates a new mbed program if executed within a non-program location.\n"
18221839
"Alternatively creates an mbed library if executed within an existing program.\n"
18231840
"When creating new program, the latest mbed-os release will be downloaded/added\n unless --create-only is specified.\n"
18241841
"Supported source control management: git, hg"))
1825-
def new(name, scm='git', program=False, library=False, mbedlib=False, create_only=False, depth=None, protocol=None):
1842+
def new(name, scm='git', program=False, library=False, mbedlib=False, create_only=False, depth=None, protocol=None, offline=False):
18261843
global cwd_root
1844+
offline_warning(offline)
18271845

18281846
d_path = os.path.abspath(name or getcwd())
18291847
p_path = os.path.dirname(d_path)
@@ -1900,14 +1918,16 @@ def new(name, scm='git', program=False, library=False, mbedlib=False, create_onl
19001918
dict(name='--depth', nargs='?', help='Number of revisions to fetch from the remote repository. Default: all revisions.'),
19011919
dict(name='--protocol', nargs='?', help='Transport protocol for the source control management. Supported: https, http, ssh, git. Default: inferred from URL.'),
19021920
dict(name='--insecure', action='store_true', help='Allow insecure repository URLs. By default mbed CLI imports only "safe" URLs, e.g. based on standard ports - 80, 443 and 22. This option enables the use of arbitrary URLs/ports.'),
1921+
dict(name='--offline', action='store_true', help='Offline mode will force the use of locally cached repositories and prevent requests to remote repositories.'),
19031922
hidden_aliases=['im', 'imp'],
19041923
help='Import program from URL',
19051924
description=(
19061925
"Imports mbed program and its dependencies from a source control based URL\n"
19071926
"(GitHub, Bitbucket, mbed.org) into the current directory or specified\npath.\n"
19081927
"Use 'mbed add <URL>' to add a library into an existing program."))
1909-
def import_(url, path=None, ignore=False, depth=None, protocol=None, insecure=False, top=True):
1928+
def import_(url, path=None, ignore=False, depth=None, protocol=None, insecure=False, offline=False, top=True):
19101929
global cwd_root
1930+
offline_warning(offline, top)
19111931

19121932
# translate 'mbed-os' to https://github.com/ARMmbed/mbed-os
19131933
orig_url = url
@@ -1931,11 +1951,11 @@ def import_(url, path=None, ignore=False, depth=None, protocol=None, insecure=Fa
19311951
error("Directory \"%s\" is not empty. Please ensure that the destination folder is empty." % repo.path, 1)
19321952

19331953
if not Repo.isurl(orig_url) and os.path.exists(orig_url):
1934-
warning("Importing from a local folder \"%s\", not from a URL" % orig_url)
1954+
warning("Importing from a local folder \"%s\", not from a URL" % orig_url)
19351955

19361956
text = "Importing program" if top else "Adding library"
19371957
action("%s \"%s\" from \"%s\"%s" % (text, relpath(cwd_root, repo.path), formaturl(repo.url, protocol), ' at '+(repo.revtype(repo.rev))))
1938-
if repo.clone(repo.url, repo.path, rev=repo.rev, depth=depth, protocol=protocol):
1958+
if repo.clone(repo.url, repo.path, rev=repo.rev, depth=depth, protocol=protocol, offline=offline):
19391959
with cd(repo.path):
19401960
Program(repo.path).set_root()
19411961
try:
@@ -1962,7 +1982,7 @@ def import_(url, path=None, ignore=False, depth=None, protocol=None, insecure=Fa
19621982
cwd_root = repo.path
19631983

19641984
with cd(repo.path):
1965-
deploy(ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, top=False)
1985+
deploy(ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, offline=offline, top=False)
19661986

19671987
if top:
19681988
Program(repo.path).post_action()
@@ -1976,17 +1996,19 @@ def import_(url, path=None, ignore=False, depth=None, protocol=None, insecure=Fa
19761996
dict(name='--depth', nargs='?', help='Number of revisions to fetch from the remote repository. Default: all revisions.'),
19771997
dict(name='--protocol', nargs='?', help='Transport protocol for the source control management. Supported: https, http, ssh, git. Default: inferred from URL.'),
19781998
dict(name='--insecure', action='store_true', help='Allow insecure repository URLs. By default mbed CLI imports only "safe" URLs, e.g. based on standard ports - 80, 443 and 22. This option enables the use of arbitrary URLs/ports.'),
1999+
dict(name='--offline', action='store_true', help='Offline mode will force the use of locally cached repositories and prevent requests to remote repositories.'),
19792000
hidden_aliases=['ad'],
19802001
help='Add library from URL',
19812002
description=(
19822003
"Adds mbed library and its dependencies from a source control based URL\n"
19832004
"(GitHub, Bitbucket, mbed.org) into an existing program.\n"
19842005
"Use 'mbed import <URL>' to import as a program"))
1985-
def add(url, path=None, ignore=False, depth=None, protocol=None, insecure=False, top=True):
1986-
repo = Repo.fromrepo()
2006+
def add(url, path=None, ignore=False, depth=None, protocol=None, insecure=False, offline=False, top=True):
2007+
offline_warning(offline, top)
19872008

2009+
repo = Repo.fromrepo()
19882010
lib = Repo.fromurl(url, path)
1989-
import_(lib.fullurl, lib.path, ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, top=False)
2011+
import_(lib.fullurl, lib.path, ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, offline=offline, top=False)
19902012
repo.ignore(relpath(repo.path, lib.path))
19912013
lib.sync()
19922014

@@ -2023,22 +2045,24 @@ def remove(path):
20232045
dict(name='--depth', nargs='?', help='Number of revisions to fetch from the remote repository. Default: all revisions.'),
20242046
dict(name='--protocol', nargs='?', help='Transport protocol for the source control management. Supported: https, http, ssh, git. Default: inferred from URL.'),
20252047
dict(name='--insecure', action='store_true', help='Allow insecure repository URLs. By default mbed CLI imports only "safe" URLs, e.g. based on standard ports - 80, 443 and 22. This option enables the use of arbitrary URLs/ports.'),
2048+
dict(name='--offline', action='store_true', help='Offline mode will force the use of locally cached repositories and prevent requests to remote repositories.'),
20262049
help='Find and add missing libraries',
20272050
description=(
20282051
"Import missing dependencies in an existing program or library.\n"
20292052
"Use 'mbed import <URL>' and 'mbed add <URL>' instead of cloning manually and\n"
20302053
"then running 'mbed deploy'"))
2031-
def deploy(ignore=False, depth=None, protocol=None, insecure=False, top=True):
2054+
def deploy(ignore=False, depth=None, protocol=None, insecure=False, offline=False, top=True):
2055+
offline_warning(offline, top)
2056+
20322057
repo = Repo.fromrepo()
20332058
repo.ignores()
2034-
20352059
for lib in repo.libs:
20362060
if os.path.isdir(lib.path):
20372061
if lib.check_repo():
20382062
with cd(lib.path):
2039-
update(lib.rev, ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, top=False)
2063+
update(lib.rev, ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, offline=offline, top=False)
20402064
else:
2041-
import_(lib.fullurl, lib.path, ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, top=False)
2065+
import_(lib.fullurl, lib.path, ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, offline=offline, top=False)
20422066
repo.ignore(relpath(repo.path, lib.path))
20432067

20442068
if top:
@@ -2108,14 +2132,17 @@ def publish(all_refs=None, msg=None, top=True):
21082132
dict(name='--depth', nargs='?', help='Number of revisions to fetch from the remote repository. Default: all revisions.'),
21092133
dict(name='--protocol', nargs='?', help='Transport protocol for the source control management. Supported: https, http, ssh, git. Default: inferred from URL.'),
21102134
dict(name='--insecure', action='store_true', help='Allow insecure repository URLs. By default mbed CLI imports only "safe" URLs, e.g. based on standard ports - 80, 443 and 22. This option enables the use of arbitrary URLs/ports.'),
2135+
dict(name='--offline', action='store_true', help='Offline mode will force the use of locally cached repositories and prevent requests to remote repositories.'),
21112136
dict(name=['-l', '--latest-deps'], action='store_true', help='Update all dependencies to the latest revision of their current branch. WARNING: Ignores lib files'),
21122137
hidden_aliases=['up'],
21132138
help='Update to branch, tag, revision or latest',
21142139
description=(
21152140
"Updates the current program or library and its dependencies to specified\nbranch, tag or revision.\n"
21162141
"Alternatively fetches from associated remote repository URL and updates to the\n"
21172142
"latest revision in the current branch."))
2118-
def update(rev=None, clean=False, clean_files=False, clean_deps=False, ignore=False, depth=None, protocol=None, insecure=False, latest_deps=False, top=True):
2143+
def update(rev=None, clean=False, clean_files=False, clean_deps=False, ignore=False, depth=None, protocol=None, insecure=False, offline=False, latest_deps=False, top=True):
2144+
offline_warning(offline, top)
2145+
21192146
if top and clean:
21202147
sync()
21212148

@@ -2148,11 +2175,13 @@ def update(rev=None, clean=False, clean_files=False, clean_deps=False, ignore=Fa
21482175
repo.revtype(rev)))
21492176

21502177
try:
2151-
repo.update(rev, clean, clean_files, repo.is_local)
2178+
repo.update(rev, clean, clean_files, offline or repo.is_local)
21522179
except ProcessException as e:
21532180
err = "Unable to update \"%s\" to %s" % (repo.name, repo.revtype(rev))
2154-
if depth:
2155-
err = err + ("\nThe --depth option might prevent fetching the whole revision tree and checking out %s." % (repo.revtype(repo.rev)))
2181+
if offline:
2182+
err = err + "\nThis might be caused by offline mode ('--offline' used). You should try without offline mode."
2183+
elif depth:
2184+
err = err + ("\nThis might be caused by the '--depth' option, which prevents fetching the whole revision history." % (repo.revtype(repo.rev)))
21562185
if ignore:
21572186
warning(err)
21582187
else:
@@ -2203,11 +2232,11 @@ def update(rev=None, clean=False, clean_files=False, clean_deps=False, ignore=Fa
22032232
# Import missing repos and update to revs
22042233
for lib in repo.libs:
22052234
if not os.path.isdir(lib.path):
2206-
import_(lib.fullurl, lib.path, ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, top=False)
2235+
import_(lib.fullurl, lib.path, ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, offline=offline, top=False)
22072236
repo.ignore(relpath(repo.path, lib.path))
22082237
else:
22092238
with cd(lib.path):
2210-
update(None if latest_deps else lib.rev, clean=clean, clean_files=clean_files, clean_deps=clean_deps, ignore=ignore, protocol=protocol, insecure=insecure, latest_deps=latest_deps, top=False)
2239+
update(None if latest_deps else lib.rev, clean=clean, clean_files=clean_files, clean_deps=clean_deps, ignore=ignore, depth=depth, protocol=protocol, insecure=insecure, offline=offline, latest_deps=latest_deps, top=False)
22112240

22122241
if top:
22132242
program = Program(repo.path)

0 commit comments

Comments
 (0)