Compare commits

...

1321 Commits

Author SHA1 Message Date
73455b4264 v8.0.1 2018-10-20 15:26:04 +02:00
28b0793fc9 Merge pull request #993 from resin-io/fix-push
Update dockerignore to fix escSL bug
2018-10-20 15:24:28 +02:00
c904726259 Update dockerignore to fix escSL bug
Change-type: patch
2018-10-20 14:54:33 +02:00
6606b65c9b v8.0.0 2018-10-19 17:31:41 +02:00
61160fd2f5 Merge pull request #991 from resin-io/v8-meta-branch
Release CLI v8
2018-10-19 17:29:40 +02:00
bf71f9ea16 Merge pull request #981 from resin-io/local-mode-v2
Local mode v2
2018-10-19 17:09:28 +02:00
fe751fdb23 Check supervisor version before attempting to do a local push
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:45:23 +02:00
947f91d570 Support multicontainer local mode in resin push
Change-type: minor
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:44:56 +02:00
c5d4e30e24 logger: Add logs logging function
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:44:53 +02:00
f560aa7523 export resolveProject function from compose module
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:44:49 +02:00
6bcfb2dd51 logs: Add log build function to logger
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:44:44 +02:00
bf062124f7 compose: Add compose typings
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:44:39 +02:00
221666f59a Stop accepting resin-compose.yml as a build composition definition
These files are not supported by any other part of the resin
infrastructure, and it could cause confusion with it not being
supported everywhere. The idea was originally added because we
thought we might need to make extensions on docker-compose, but
that hasn't happened.

Change-type: major
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:43:49 +02:00
4369a2d161 tconfig: Add skipLibCheck to tsconfig
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:43:46 +02:00
cd6ee4ef5e Send push source packages as gzipped data
Change-type: minor
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:43:39 +02:00
872b17cf24 refactor: Allow setting of a remote build error message
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:43:32 +02:00
88e11347bc tests: Add tests for ignore files
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:43:28 +02:00
a3dd489c70 Respect ignore files when tarring sources
This commit brings in the ignore and dockerignore libraries, which when
provided with the patterns in the aforementioned files will ignore them.

Change-type: major
Closes: 889
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:43:22 +02:00
0c1c108b2b Check for correct architecture when preloading, instead of correct device type
Preload will now propose to preload any app that matches the image
architecture.

Change-type: major
Signed-off-by: Alexis Svinartchouk <alexis@resin.io>
2018-10-19 16:43:02 +02:00
f02ed43f33 Default preload boolean parameters to false
Change-type: patch
Signed-off-by: Alexis Svinartchouk <alexis@resin.io>
2018-10-19 16:42:51 +02:00
63c3d7ceee fix: Apply prettier to merged files
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:42:48 +02:00
dac45a884e dev: Add fast test npm task, to speed development
Currently running the tests is painfully slow, this commit adds a task
which will run the bare minimum build, and then the tests, speeding up
the process by an order of magnitude.

I had to repeat `gulp test`, instead of reusing `npm run test`, so that
the pretest task isn't ran too.

Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:42:42 +02:00
ec589c2639 Correctly error out on failed remote builds
The push command was relying on the output from the builder to indicate
the build status, but this isn't helpful for CI. This commit makes the
remote build module respect the `isError` flag which the builder sends
in any errors. Any errors which come from the builder indicate the
release will not be deployed.

Change-type: patch
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:42:14 +02:00
f65e777d1b Bump tsconfig target to es6
Change-type: major
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-10-19 16:42:06 +02:00
684ac9fa24 v7.10.9 2018-10-18 21:08:35 +02:00
330cbc6a68 Merge pull request #989 from resin-io/sdk-references-update
Update sdk references in wizzard.coffee
2018-10-18 21:06:38 +02:00
14bfca8c3a v7.10.8 2018-10-18 20:14:43 +02:00
20c07d31b2 Merge pull request #987 from resin-io/sdk-references-update
Update sdk references in device.coffee
2018-10-18 20:12:08 +02:00
64b4f67477 Update sdk references in wizzard.coffee
Change-type:patch
2018-10-18 18:53:03 +02:00
a8ceadc300 v7.10.7 2018-10-18 17:25:57 +02:00
973d25f467 Merge pull request #986 from resin-io/sdk-references-update
Update sdk sdk references in auth.coffee
2018-10-18 17:24:02 +02:00
0d06701e2f Update sdk references in notes.coffee
Change-type:patch
2018-10-18 16:22:35 +02:00
379f1cc217 Update sdk references in device.coffee
Change-type:patch
2018-10-18 16:08:29 +02:00
7b7ae4ff89 Update sdk sdk references in auth.coffee
Change-type:patch
2018-10-18 14:51:03 +02:00
8e83a401eb v7.10.6 2018-10-03 06:58:41 -07:00
2d1891a182 Merge pull request #976 from resin-io/975-fix-preload-examples
Fix formatting of preload examples
2018-10-03 15:56:56 +02:00
8df066df12 Fix formatting of preload examples
Based on https://github.com/resin-io/docs/pull/915 from @drjasonharrison-vp-eio

Change-type: patch
Signed-off-by: Tim Perry <tim@resin.io>
2018-10-03 15:31:24 +02:00
bd59f95e1a v7.10.5 2018-09-25 07:09:26 -07:00
2b982a1c0c Merge pull request #972 from resin-io/readme-typo
README: Fix typo
2018-09-25 15:08:01 +01:00
ab64fbc904 README: Fix typo
Change-type: patch
Signed-off-by: Lucian Buzzo <lucian.buzzo@gmail.com>
2018-09-25 13:35:35 +01:00
733b98f072 v7.10.4 2018-09-24 10:08:55 -07:00
7c538a3658 Merge pull request #967 from resin-io/966-register-print-uuid
device: When registering, print the uuid
2018-09-24 19:07:15 +02:00
8298ba5765 device: When registering, print the uuid
This restores the behavior from before #911,
which is useful from some users.

Closes #966

Change-type: patch
Signed-off-by: Pablo Carranza Velez <pablocarranza@gmail.com>
2018-09-24 15:18:40 +02:00
33a23773d8 v7.10.3 2018-09-19 09:17:52 -07:00
21a3b82845 Merge pull request #971 from resin-io/add-emulated-to-build-docs
Include --emulated in the example resin build parameters
2018-09-19 18:16:28 +02:00
8688eb5da0 Include --emulated in the example resin build parameters
Change-type: patch
Signed-off-by: Tim Perry <tim@resin.io>
2018-09-19 15:34:29 +02:00
5b0ea9673f v7.10.2 2018-09-18 09:17:50 -07:00
44fd8adeba Merge pull request #970 from resin-io/969-resin-semver
dependencies: Update resin-semver version to support Balena OS
2018-09-18 17:15:09 +01:00
a5e03d55c3 dependencies: Update resin-semver version to support Balena OS
Connects to #969

Change-type: patch
Signed-off-by: Lucian Buzzo <lucian.buzzo@gmail.com>
2018-09-18 14:23:10 +01:00
80629322ea v7.10.1 2018-09-11 05:29:41 -07:00
946efbcb7f Merge pull request #965 from resin-io/964-drop-npm-deploy
Stop Travis deploying to npm (now handled by concourse)
2018-09-11 14:28:11 +02:00
be8a314d2b Stop Travis deploying to npm (now handled by concourse)
Change-type: patch
Signed-off-by: Tim Perry <tim@resin.io>
2018-09-11 13:58:06 +02:00
0a7203cafe v7.10.0 2018-09-11 04:21:19 -07:00
786fed0151 Merge pull request #963 from resin-io/update-resin-cli-form
Update resin-cli-form to 2.x
2018-09-11 12:17:22 +01:00
9cd8228a20 Update resin-cli-form to 2.x
Change-type: minor
Signed-off-by: Pagan Gazzard <page@resin.io>
2018-09-10 18:31:51 +01:00
652b5f22dd v7.9.4 2018-09-10 06:34:48 -07:00
eed3c06789 Merge pull request #911 from resin-io/fix_pre_provision
Device api keys are no longer used in the registration process
2018-09-10 15:33:37 +02:00
3b283d4a98 Device api keys are no longer used in the registration process
Change-type: patch
Signed-off-by: Theodor Gherzan <theodor@resin.io>
2018-09-10 12:30:51 +01:00
bc6b5ba7b3 Auto-merge for PR #952 via VersionBot
Fix configuration hangs with some images using a larger threadpool
2018-08-20 15:37:22 +00:00
74789ae88f v7.9.3 2018-08-20 15:29:02 +00:00
295d6dee74 Fix configuration hangs with some images by expanding the threadpool
Change-type: patch
Signed-off-by: Tim Perry <tim@resin.io>
2018-08-20 17:06:26 +02:00
5010a1e312 Auto-merge for PR #946 via VersionBot
Add warning about re-enabling automatic updates
2018-08-15 21:39:23 +00:00
3c2f7ea622 v7.9.2 2018-08-15 21:31:24 +00:00
94f02f0ad8 Add warning about re-enabling automatic updates
Change-type: patch
Signed-off-by: Pagan Gazzard <page@resin.io>
2018-08-15 14:20:11 -07:00
375f84b24e Auto-merge for PR #942 via VersionBot
Fix errors in `getRequestStream` not being propogated
2018-08-15 18:08:59 +00:00
06c649dfd0 v7.9.1 2018-08-15 17:59:46 +00:00
71eca70a22 Fix errors in getRequestStream not being propogated
Change-type: patch
Signed-off-by: Pagan Gazzard <page@resin.io>
2018-08-14 18:21:10 -07:00
53c7bc622c Auto-merge for PR #903 via VersionBot
Support emulated and nocache options for remote builds
2018-08-09 14:52:03 +00:00
975ae45e49 v7.9.0 2018-08-09 14:42:30 +00:00
e7c68c1a5c Support emulated and nocache options for remote builds
Change-type: minor
Closes: #901
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-08-09 14:36:34 +01:00
5beeb78220 Auto-merge for PR #939 via VersionBot
Fix bug where the sudo helper failed in os initialize
2018-08-09 10:37:53 +00:00
c90ba7aa0f v7.8.6 2018-08-09 10:29:50 +00:00
802ccc1b9a Fix bug where the sudo helper failed in os initialize
Change-type: patch
Signed-off-by: Tim Perry <tim@resin.io>
2018-08-09 12:11:26 +02:00
b6ef251625 Auto-merge for PR #934 via VersionBot
Add an env vars example config to the local push docs
2018-08-09 10:09:37 +00:00
fd707d6a07 v7.8.5 2018-08-09 10:01:55 +00:00
392cd8569f Make build trigger hash examples clearer
Signed-off-by: Tim Perry <tim@resin.io>
2018-08-09 11:47:21 +02:00
e32eda26d9 Update .resin-sync.yml docs for local push and include example env vars
Change-type: patch
Signed-off-by: Tim Perry <tim@resin.io>
2018-08-09 11:21:45 +02:00
d8aaccf80c Update typed-error to fix some TS complaints
Signed-off-by: Tim Perry <tim@resin.io>
2018-08-09 11:21:45 +02:00
d5fd5f5f2d Auto-merge for PR #936 via VersionBot
Update klaw now that the fork changes has been finished & released
2018-08-02 10:37:40 +00:00
2cb69c12f1 v7.8.4 2018-08-02 10:29:33 +00:00
7c75346a1a Update klaw
The changes from our fork have now been completed and released

Change-type: patch
Signed-off-by: Tim Perry <tim@resin.io>
2018-08-01 16:43:26 +02:00
148d15b6d9 Auto-merge for PR #931 via VersionBot
Follow links found during builds
2018-07-25 14:07:19 +00:00
a46a79df59 v7.8.3 2018-07-25 13:58:28 +00:00
e350f9b335 Follow links found during builds
Change-Type: patch
2018-07-25 12:38:17 +02:00
bd00773f1b Auto-merge for PR #929 via VersionBot
Update reconfix to fix volume signature errors in local configure
2018-07-25 10:23:37 +00:00
ef3c7f0fd6 v7.8.2 2018-07-25 10:13:48 +00:00
f4f44f978e Update reconfix to fix volume signature errors in local configure
Change-Type: patch
2018-07-24 20:57:40 +02:00
442416efc3 Auto-merge for PR #930 via VersionBot
Be explicit about how much initial history log tailing includes
2018-07-20 18:07:00 +00:00
ef33ffedcf v7.8.1 2018-07-20 17:38:09 +00:00
430d4aeaa7 Be explicit about how much initial history log tailing includes
Change-Type: patch
2018-07-20 16:32:31 +02:00
171632f83f Auto-merge for PR #895 via VersionBot
Add join/leave commands to promote and move devices between platforms
2018-07-20 12:36:20 +00:00
1fa7141b58 v7.8.0 2018-07-20 10:40:22 +00:00
916cc36430 Lazily import resin-image-fs
If for whatever reason resin-image-fs is not importable — eg. if it’s built for another arch — any command that imports `helpers.ts` will just quit without any error/traceback.
2018-07-20 13:04:26 +03:00
27b877dd33 Forward root CA to device config if one is present 2018-07-19 22:34:31 +03:00
5cbe1c410f Add join/leave commands to promote and move devices between platforms
Both commands work with local devices by remotely invoking the `os-config` executable via SSH. This requires an as of yet unreleased resinOS (that will most likely be v2.14) and the commands ascertain compatibility merely by looking for the `os-config` executable in the device, and bail out if it’s not present.

`join` and `leave` accept a couple of optional arguments and implement a wizard-style interface if these are not given. They allow to interactively select the device and the application to promote to. If the user has no apps, `join` will offer the user to create one. `join` will also offer the user to login or create an account if they’re not logged in already without exiting the wizard.

`resin-sync` (that's used internally to discover local devices) requires admin privileges. If no device has been specified as an argument, the commands will launch the device scanning process in a privileged subprocess via two new internal commands: `internal sudo` and `internal scanDevices`. This avoids having the user to invoke the commands with sudo and only request escalation if truly needed. This commit also removes the dependency to “president”, implementing “sudo” functionality within the CLI.

Change-Type: minor
2018-07-19 22:18:02 +03:00
7846af390e Improve selectFromList function signature to be much more reusable 2018-07-19 21:53:43 +03:00
79d9ebc805 Auto-merge for PR #923 via VersionBot
Update OS & config actions to the MC SDK, and add a --version option
2018-07-17 15:43:30 +00:00
25b853c535 v7.7.4 2018-07-17 15:35:26 +00:00
a93141343f Update TypeScript to 2.8.1
Change-Type: patch
2018-07-17 16:48:14 +02:00
9a467c5ecd Pin all type modules 2018-07-17 15:59:31 +02:00
70be2ae596 Tweaks to config options handling after review 2018-07-17 15:38:38 +02:00
36eb0a108e Post-review tweaks to OS actions 2018-07-13 19:34:59 +02:00
0bf6fb1739 Add --version options to os configure & config generate
This is used to ensure the correct type of API key is used in all
configuration.

Change-Type: patch
2018-07-13 19:34:59 +02:00
892adf4c47 Update OS & config actions to the latest SDK
Fixes #915
Change-Type: patch
2018-07-13 19:34:59 +02:00
5d1d004b72 Auto-merge for PR #927 via VersionBot
Update the CLI deploy key since npm invalidated the old one
2018-07-13 17:21:18 +00:00
dea5a60b2d v7.7.3 2018-07-13 17:05:32 +00:00
652a1b7650 Update the deploy key since npm invalidated the old one
Change-Type: patch
2018-07-13 16:39:56 +02:00
350843af1e Auto-merge for PR #926 via VersionBot
Pin ext2fs to 1.0.7 to avoid temporary deployment issues
2018-07-13 11:40:25 +00:00
e04c4a8ee3 v7.7.2 2018-07-13 11:33:13 +00:00
9d0c3f7535 Pin ext2fs to 1.0.7 to avoid temporary deployment issues
Change-Type: patch
2018-07-13 13:20:53 +02:00
9561d4da2e Auto-merge for PR #925 via VersionBot
Update logs to use new v10 MC SDK
2018-07-12 13:59:28 +00:00
8296dcf946 v7.7.1 2018-07-12 13:52:10 +00:00
e62e8b88c2 Simplify logs promises after review 2018-07-12 15:38:27 +02:00
4388a248b9 Make sure we don't duplicate historical logs when streaming 2018-07-12 15:23:33 +02:00
f9cf0aaf23 Remove a couple of artifacts of the pubnub logs implementation 2018-07-12 15:10:16 +02:00
dc9ee09838 Update CLI to SDK v10 (include new API logs)
Change-Type: patch
2018-07-12 01:03:16 +02:00
7cb27283c5 Update logs action to use the MC SDK 2018-07-12 01:03:16 +02:00
10a9840b34 Auto-merge for PR #921 via VersionBot
Add --generate-device-api-key parameter to config generate
2018-07-11 04:28:25 +00:00
ce3e04bfe8 v7.7.0 2018-07-11 04:21:42 +00:00
52f93f8f12 Add --generate-device-api-key parameter to config generate
Change-Type: minor
2018-07-10 19:57:56 +02:00
af9e1a122d Auto-merge for PR #910 via VersionBot
Make local commands more resilient to unnamed containers
2018-06-28 16:26:11 +00:00
9017b8ec11 v7.6.2 2018-06-28 12:55:34 +00:00
bf4f687a2a Make local commands more resilient to unnamed containers
Change-Type: patch
2018-06-28 12:34:31 +02:00
9d4e6eb825 Auto-merge for PR #907 via VersionBot
Make sure 'resin push' is included in the CLI docs
2018-06-26 17:22:44 +00:00
fba4afb7d2 v7.6.1 2018-06-26 17:15:20 +00:00
8c74f784f7 Make sure 'resin push' is included in the docs
Fixes #906
Change-Type: patch
2018-06-26 19:00:20 +02:00
69ca1ffa59 Auto-merge for PR #896 via VersionBot
Support pinned release preloading
2018-06-20 17:00:12 +00:00
7d1b00877e v7.6.0 2018-06-20 16:50:01 +00:00
1a48fed1f7 Support pinned release preloading
Change-type: minor
Closes: #886
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-06-13 12:29:30 +01:00
bc86359e63 Auto-merge for PR #893 via VersionBot
Document Python native build dependency
2018-06-12 18:33:47 +00:00
f6822f1502 v7.5.2 2018-06-12 18:26:12 +00:00
398c34d842 Includes new prettier changes, and pin prettier to stop more appearing 2018-06-12 17:43:15 +02:00
72a893be95 Document Pyhton native build dependency
Change-Type: patch
2018-06-12 17:11:45 +02:00
7b23b0e103 Auto-merge for PR #887 via VersionBot
Add a multicontainer caveat to the env var commands
2018-06-01 11:10:33 +00:00
0ce7878042 v7.5.1 2018-06-01 10:49:15 +00:00
da8483e6a6 Add a multicontainer caveat to the env var commands
Change-Type: patch
2018-06-01 12:37:29 +02:00
16f70fd946 Auto-merge for PR #883 via VersionBot
Update resin-compose-parse dependency version
2018-05-31 16:16:47 +00:00
78aa898b37 v7.5.0 2018-05-31 16:07:38 +00:00
b7f94a222d Update resin-compose-parse dependency version to 1.10.2
Change-type: minor
2018-05-30 11:57:04 -03:00
7bea2c26b8 Auto-merge for PR #879 via VersionBot
Update SDK for device commands, so we show new device dashboard URLs
2018-05-24 14:13:01 +00:00
7c178b8095 v7.4.1 2018-05-24 14:03:02 +00:00
865f085094 Make sure we still show the device commit, despite API changes 2018-05-24 14:43:45 +01:00
28fe69fe94 Update to latest SDK in lots of easy device commands 2018-05-18 20:05:24 +02:00
232cf8d426 Update SDK in resin device(s) to ensure the dashboard URL is correct
Fixes #768

Change-Type: patch
2018-05-18 20:00:40 +02:00
22e74983b0 Auto-merge for PR #868 via VersionBot
Add push command which starts a build on remote resin servers
2018-05-10 12:44:43 +00:00
c88dd2257a v7.4.0 2018-05-10 12:28:32 +00:00
439d8d396f Add push command which starts a build on remote resin servers
Change-type: minor
Connects-to: #843
2018-05-10 11:43:45 +01:00
6d8086c09b Auto-merge for PR #874 via VersionBot
Handle failed requires & missing bindings
2018-05-03 17:56:53 +00:00
e85f252f29 v7.3.8 2018-05-03 17:49:06 +00:00
4b818ad51c Style improvements after review 2018-05-03 18:59:28 +02:00
c2518448a3 Catch require errors and provide helpful instructions
Change-Type: patch
2018-05-03 16:01:40 +02:00
e7a8deed05 Inline the entire resin-cli-errors module
It's awkward that error handling requires you to go to a different
package, it makes things more complicated, and there's nowhere else that
really should be reusing this logic. Let's inline it, so we can
deprecate the module entirely.

Change-Type: patch
2018-05-03 15:15:03 +02:00
0ac599d20c Auto-merge for PR #871 via VersionBot
Pin node types to v9.0.0 to avoid build errors with transient dependencies
2018-04-30 15:25:23 +00:00
7d7074e6b7 v7.3.7 2018-04-30 15:18:31 +00:00
35ca34d07d Pin node types to v9.0.0 to avoid build errors with transient dependencies
Change-type: patch
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-04-30 16:09:12 +01:00
90d7316b4c Auto-merge for PR #870 via VersionBot
Update resin-image-fs to stop non-config commands failing in node 10
2018-04-30 09:54:09 +00:00
904b4e96d9 v7.3.6 2018-04-30 09:34:40 +00:00
2c46c59a79 Update resin-image-fs to stop non-config commands failing in node 10
This doesn't fix actual usage of image fs, just makes it possible to
stop commands that don't use it from failing entirely.

Connects-To: #869
Change-Type: patch
2018-04-30 11:14:39 +02:00
297ff86895 Auto-merge for PR #858 via VersionBot
Don't show Docker container status from devices, as it can be wrong
2018-04-18 19:08:16 +00:00
a154401424 v7.3.5 2018-04-18 19:00:21 +00:00
ad2713fc00 Don't show Docker container status from devices, as it can be wrong
The status includes a description of how long the device has been in
this state (Up 6 weeks), which is frequently wrong as when the device
first starts up its clock isn't up to date. It's confusing and messy,
best to just remove it entirely.

Fixes #828
Change-Type: patch
2018-04-18 20:16:44 +02:00
6388cfaf40 Auto-merge for PR #865 via VersionBot
Include resin compose schemas in the standalone build
2018-04-18 16:41:50 +00:00
167f38e342 v7.3.4 2018-04-18 16:27:52 +00:00
919b3c3435 Include resin compose schemas in the standalone build
Fixes #844
Change-Type: patch
2018-04-18 13:34:35 +02:00
2e1ab22173 Auto-merge for PR #861 via VersionBot
727 sentry improvements
2018-04-17 14:46:13 +00:00
0a23563d7e v7.3.3 2018-04-17 14:01:51 +00:00
37e4ec6364 Rename expectedError to exitWithExpectedError 2018-04-17 15:18:06 +02:00
6a8b947c2e Don't report lots of user input errors
Change-Type: patch
2018-04-17 15:18:06 +02:00
a16ac37625 Include Sentry breadcrumbs for context in error reports
Change-Type: patch
2018-04-17 15:18:06 +02:00
cf4c7826b2 Update to Sentry 2.x
Change-Type: patch
2018-04-17 15:18:06 +02:00
a0a26f0a1e Auto-merge for PR #862 via VersionBot
Update Dockerode to fix local push issue in standalone builds
2018-04-16 16:21:22 +00:00
a921139a12 v7.3.2 2018-04-16 15:21:33 +00:00
36da7b66c8 Update Dockerode to fix local push issue in standalone builds
Connects-To: #824
Change-Type: patch
2018-04-16 16:43:17 +02:00
3aa87544eb Auto-merge for PR #849 via VersionBot
Update resin-compose-parse to v1.8.1 to fix a problem parsing ports
2018-04-13 19:43:58 +00:00
6121fa505e v7.3.1 2018-04-13 19:38:01 +00:00
a5ba5befd1 Update resin-compose-parse to v1.8.1 to fix a problem parsing ports
Connects-to: https://github.com/resin-io/resin-supervisor/issues/618

Change-Type: patch
Signed-off-by: Pablo Carranza Velez <pablo@resin.io>
2018-04-13 11:17:18 -07:00
b7214a306c Auto-merge for PR #854 via VersionBot
Add 'api-key generate' command
2018-04-12 10:24:39 +00:00
d7616e941a v7.3.0 2018-04-12 10:06:09 +00:00
834a2f1e4d Warn user that api keys will not be shown again in future 2018-04-11 19:31:03 +02:00
0e5f2fe748 Remove now-unused stream-to-promise dependency 2018-04-11 19:30:29 +02:00
e0bcb5e0b9 Always call done() for api key generation, not just if we're successful 2018-04-11 19:27:58 +02:00
59d4890eae Add 'api-key generate' command
Change-Type: minor
2018-04-10 19:21:37 +02:00
51da5360da Auto-merge for PR #852 via VersionBot
Explicitly depend on tar-stream
2018-04-10 14:14:38 +00:00
2655aef28b v7.2.4 2018-04-10 13:49:09 +00:00
45d3a7a124 Explicitly depend on tar-stream
Change-Type: patch
2018-04-10 13:10:25 +02:00
662e4f8940 Merge pull request #853 from resin-io/document-version-rec
Correct documented node version requirement to 6+
2018-04-10 13:10:02 +02:00
c06993cb8e Correct documented node version requirement to 6+
Change-Type: patch
2018-04-09 16:55:36 +02:00
a650f30ce8 Auto-merge for PR #847 via VersionBot
Add a fast build script to package.json
2018-04-06 17:11:31 +00:00
0a924b2dcb v7.2.3 2018-04-06 16:27:32 +00:00
89f62683ce Add a fast build script to package.json
This doesn't run a linter or any documentation generation, aiding in
quick development time.

Change-type: patch
Signed-off-by: Cameron Diver <cameron@resin.io>
2018-04-06 15:40:08 +01:00
143d88f3df Auto-merge for PR #846 via VersionBot
Throw a clear error when logging in with an invalid token
2018-04-04 19:34:56 +00:00
d166a65422 v7.2.2 2018-04-04 18:56:26 +00:00
dd268993b3 Throw a clear error when logging in with an invalid token
Change-Type: patch
2018-04-04 15:43:34 +02:00
13a35b288f Auto-merge for PR #839 via VersionBot
Update docker-qemu-transpose to avoid the broken 0.4.1 release
2018-03-29 14:47:30 +00:00
81e653d31b v7.2.1 2018-03-29 13:52:06 +00:00
875ec8b8bd Update docker-qemu-transpose to avoid the broken 0.4.1 release
Change-Type: patch
2018-03-29 15:28:56 +02:00
989df9b857 Auto-merge for PR #835 via VersionBot
Initial support for api keys in the CLI
2018-03-29 10:15:31 +00:00
0829d3c176 v7.2.0 2018-03-29 10:09:08 +00:00
ce64889b04 Clarify isTokenValid logic 2018-03-29 11:11:25 +02:00
d3a0bfc5f6 Fix auth utils tests to work with new SDK 2018-03-29 11:11:25 +02:00
e965c603d2 Use spec test reporter, so we can debug with output 2018-03-29 11:11:25 +02:00
0e2fb8c96c Promisify auth utils tests 2018-03-29 11:11:25 +02:00
2db1d84d3c Do not require a login for builds
Fixes: #578
Change-Type: patch
2018-03-29 11:11:25 +02:00
12a1916007 Allow (experimental!) login with API keys
Change-Type: minor
2018-03-29 11:11:25 +02:00
b4526e9895 Auto-merge for PR #838 via VersionBot
Fix build emulation for multi-stage builds
2018-03-29 09:03:40 +00:00
a2d867c860 v7.1.6 2018-03-29 08:56:07 +00:00
05b1c37379 Fix build emulation for multi-stage builds
Fixes #814
Change-Type: patch
2018-03-29 10:18:31 +02:00
906cfe9268 Auto-merge for PR #834 via VersionBot
Fix crash when an app is not specified for build command
2018-03-28 12:01:10 +00:00
3c8054faa7 v7.1.5 2018-03-27 17:51:36 +00:00
c6c9046826 Fix crash when an app is not specified for build command
This is a regression introduced in #818

Change-Type: patch
2018-03-27 19:12:31 +03:00
2bbbbf6fdd Auto-merge for PR #832 via VersionBot
Upgrade resin-sync to pull in the fix for #824
2018-03-26 16:31:46 +00:00
9cce4001af v7.1.4 2018-03-26 16:09:22 +00:00
2e944cf2f4 Upgrade resin-sync to pull in the fix for #824
Change-Type: patch
2018-03-26 17:39:47 +02:00
2b0143775c Auto-merge for PR #831 via VersionBot
Prefix all pine options with '$' in preload to avoid pine warnings.
2018-03-23 15:57:54 +00:00
49fec7d8f2 v7.1.3 2018-03-23 15:49:16 +00:00
ca1ac2bb83 Prefix all pine options with '$' in preload to avoid pine warnings.
Change-Type: patch
2018-03-23 15:20:18 +00:00
50b1a7e6b0 Auto-merge for PR #830 via VersionBot
Update resin-preload to 6.2.0 and resin-sdk to 9.0.0-beta16
2018-03-23 13:56:14 +00:00
69ce2c0473 v7.1.2 2018-03-23 13:49:24 +00:00
a3b446dbe7 Update resin-preload to 6.2.0 and resin-sdk to 9.0.0-beta16
Change-Type: patch
2018-03-23 13:41:16 +00:00
1032d9927f Auto-merge for PR #827 via VersionBot
Remove explicit anchor links in CLI docs
2018-03-22 18:03:07 +00:00
12e8a50abc v7.1.1 2018-03-22 17:06:07 +00:00
a4142097f8 Merge branch 'master' into doc-headings 2018-03-22 09:17:32 -05:00
b388ccb6f3 Auto-merge for PR #818 via VersionBot
Restore legacy deployment method
2018-03-22 11:43:59 +00:00
e011502b7e v7.1.0 2018-03-22 11:36:41 +00:00
4f167cb836 Address review feedback 2018-03-22 13:26:47 +02:00
9455d438e2 Formatting fixes 2018-03-22 13:26:47 +02:00
a356ecf9b6 Remove unused code 2018-03-22 13:26:47 +02:00
066ac591ac Warn early if deploying a multicontainer project to an incompatible app
Change-Type: patch
2018-03-22 13:26:47 +02:00
62f006b89a Add legacy deploy method back
This mostly reverts the removal of the legacy deploy code that pushed image tars via the builder. It’s needed for users to avoid having to switch between CLI versions in order to push to legacy apps as well.

Note: this pins resin-sdk to 9.0.0-beta14 as I couldn’t get it to install otherwise — npm would always install 9.0.0-beta9 instead.

Change-Type: minor
2018-03-22 13:26:47 +02:00
ee75ff2753 Remove explicit anchor links in CLI docs
Our docs markdown renderer doesn't process explicit anchor tags, as it generates its own. The script that generates the markdown has been updated to not include these tags and to properly build the TOC links.

Change-type: patch
2018-03-20 13:10:07 -05:00
e4c9defb70 Auto-merge for PR #821 via VersionBot
Update resin-preload to 6.1.2
2018-03-20 15:54:28 +00:00
bb102c1918 v7.0.7 2018-03-20 15:44:13 +00:00
24ebe2946c Update resin-preload to 6.1.2
Connects-To: #820

Change-Type: patch
2018-03-20 15:22:59 +00:00
ba82b1fa27 Auto-merge for PR #815 via VersionBot
Build/deploy commands improvements
2018-03-20 10:43:31 +00:00
e3b145e7b7 v7.0.6 2018-03-20 10:33:01 +00:00
242c3731ee Remove redundant import 2018-03-19 20:52:51 +02:00
5f7eee8eac Make sure image name is all lowercase
Change-Type: patch
2018-03-19 20:52:51 +02:00
1833f6ff0a Improve handling of build log output
This makes sure build logs don’t leak escape sequences and new lines and they don’t break the output. Also improved “inline” logs by normalising the stream before passing it to “transpose build stream”.

Fixes: #808
Change-Type: patch
2018-03-19 20:52:51 +02:00
e5fb954645 Auto-merge for PR #801 via VersionBot
add bash completions
2018-03-15 20:03:33 +00:00
13f76dc020 v7.0.5 2018-03-15 18:51:46 +00:00
b409bdcc73 add blurb about bash completion
Add brief information about tab completions for bash and instructions to enable it.
2018-03-15 18:06:04 +01:00
8c3cb3f585 Add bash completions
This contains bash completion functionality for the resin CLI, including completion for sub-commands.

Change-type: patch
2018-03-15 18:05:50 +01:00
76a8b4df50 Auto-merge for PR #813 via VersionBot
Properly generate consistent working anchors for both our md output & resin docs
2018-03-15 12:09:16 +00:00
a03680311d v7.0.4 2018-03-15 12:01:05 +00:00
6ee36cb5c7 Generate consistent working anchors for both our md output & resin docs
Change-Type: patch
2018-03-15 11:40:29 +01:00
5625326c65 Auto-merge for PR #812 via VersionBot
Fix getting window size when there’s no TTY attached
2018-03-15 08:54:19 +00:00
b912419839 v7.0.3 2018-03-15 08:47:28 +00:00
fe01ead023 Fix getting window size when there’s no TTY attached
Change-Type: patch
2018-03-15 10:30:54 +02:00
229c105d0c Auto-merge for PR #807 via VersionBot
Update full CLI docs with recent installation improvements too
2018-03-13 12:00:31 +00:00
b6e044345f v7.0.2 2018-03-13 10:47:55 +00:00
d9906121e1 Update full CLI docs with recent installation improvements too
Change-Type: patch
2018-03-12 22:17:20 +01:00
3e019f7f34 Remove leftover capitanodoc.coffee file (it's now TS) 2018-03-12 20:06:44 +01:00
eb34cb6f27 Auto-merge for PR #805 via VersionBot
Recommend unsafe-perm to fix some install issues and cleanup dependencies after MC
2018-03-12 16:36:28 +00:00
3a3178bcb9 v7.0.1 2018-03-12 15:36:10 +00:00
cdf6580ecc Recommend using unsafe-prem to avoid permission issues on install
Change-Type: patch
2018-03-12 13:36:24 +01:00
c42bc74f1f Remove unnecessary resin-cli-auth dependency
Change-Type: patch
2018-03-12 11:41:58 +01:00
35fd79f577 Remove (duplicated) runtime ts-node dependency 2018-03-12 11:41:14 +01:00
4ef0682e5a Auto-merge for PR #792 via VersionBot
Multicontainer
2018-03-09 22:12:00 +00:00
d0b7047189 v7.0.0 2018-03-09 22:04:51 +00:00
ae3f936b66 Update resin-preload to v6.0.0 2018-03-09 21:53:34 +00:00
1ef492809b Update resin-preload to v6.0.0-beta11 2018-03-09 20:40:13 +00:00
5bf9dd3a9d Update resin-preload to v6.0.0-beta10 2018-03-09 17:50:20 +00:00
b18a66f66b Update resin-preload to v6.0.0-beta9 2018-03-09 17:02:44 +00:00
1dadfdc699 Fix some formatting to make prettier+resin-lint happy 2018-03-07 16:16:07 +01:00
14a3f51b73 Add docker-compose-aware builds and deployments
Legacy behaviour is mostly retained. The most notable change in behaviour is that invoking `resin deploy` without options is now allowed (see help string how it behaves).

In this commit there are also the following notable changes:

- Deploy/Build are promoted to primary commands
- Extracts QEMU-related code to a new file
- Adds a utility file to retrieve the CLI version and its parts
- Adds a helper that can be used to manipulate display on capable clients
- Declares several new dependencies. Most are already indirectly installed via some dependency

Change-Type: minor
2018-03-07 14:48:05 +00:00
96116aeaec Fix invoking undefined method
Have no idea how this used to work.
2018-03-07 14:47:16 +00:00
7fd31b6a64 Update YAML parser
New version is 3.10.0
2018-03-07 14:47:16 +00:00
299bc0db13 Update docker-toolbelt
New version is 3.1.0.

The updated version is not backwards compatible as it removes all *Async methods that are in wide use in the CLI. The workaround for now is to manually promisify the client and replace all `new Docker()` calls with a shared function that returns a promisified client.
2018-03-07 14:47:15 +00:00
4b9ccae442 Update bundle-resolve and docker-build to latest
This brings in maintainance improvements.

New versions are:

- resin-bundle-resolve: 0.5.1
- resin-docker-build: 0.6.2
2018-03-07 14:46:35 +00:00
079ce552e3 *BREAKING*: Remove support for plugins entirely
There are very few plugins in real-world use, we're not actively working
on this at all, and the current approach won't work once we move to
standalone node-less binary installation anyway.

Change-Type: major
2018-03-07 14:46:35 +00:00
163684e3a9 Update dashboard login to use the multicontainer SDK
Change-Type: patch
2018-03-07 14:46:35 +00:00
f698f561c9 Multicontainer preload: Update resin-preload to 6.0.0-beta4
Change-Type: minor
2018-03-07 14:46:35 +00:00
cb207f18a5 Update the keys action to use the multicontainer SDK
Change-Type: patch
2018-03-07 14:46:34 +00:00
76a5cdc977 Require multicontainer SDK
* require('resin-sdk') => multicontainer SDK
 * require('resin-sdk-preconfigured') => 6.15.0 SDK
 * all 'resin-sdk' requires replaced with 'resin-sdk-preconfigured'
 * resin-sdk-preconfigured TS typings are copy pasted from the current resin-sdk master

The idea is to progressively replace all 'resin-sdk-preconfigured'
requires with 'resin-sdk' (multicontainer sdk) and eventually remove
resin-sdk-preconfigured from package.json.

Change-Type: patch
2018-03-07 14:46:31 +00:00
a82af1d2d1 Auto-merge for PR #802 via VersionBot
Fix CLI prettier configuration to avoid linting errors
2018-03-07 14:46:08 +00:00
ac7d51ad80 v6.13.5 2018-03-07 14:38:49 +00:00
797a739c92 Fix prettier configuration to avoid linting errors
Change-Type: patch
2018-03-05 16:02:09 +01:00
666b59b463 Auto-merge for PR #796 via VersionBot
Fix issue where emulated builds broke Docker `ENV` commands
2018-02-22 18:30:14 +00:00
a83d9a070c v6.13.4 2018-02-22 18:23:29 +00:00
7637377471 Fix issue where emulated builds broke Docker ENV commands
Connects-to: #795
Change-type: patch
2018-02-22 18:12:17 +00:00
6515f88d92 Auto-merge for PR #793 via VersionBot
Tweak TS & add missing deps that may cause build failures in some envs
2018-02-20 22:07:21 +00:00
92534b9c82 v6.13.3 2018-02-20 21:30:39 +00:00
c12360daa8 Tweak TS & add missing deps that may cause build failures in some envs
Connects-To: #765
Change-Type: patch
2018-02-20 20:26:18 +01:00
3d28118f3e Auto-merge for PR #794 via VersionBot
Ensure login does not wait for the browser process to close
2018-02-20 19:00:32 +00:00
04adfde064 v6.13.2 2018-02-20 17:12:45 +00:00
d8aabfd448 Ensure login does not wait for the browser process to close
Unclear why, but for some reason this only actually blocked on the
browser on OSX.

Connects-To: #791
Change-Type: patch
2018-02-16 17:28:19 +01:00
cf95870d9d Auto-merge for PR #786 via VersionBot
Assorted tiny fixes
2018-02-07 12:07:01 +00:00
55f8876bcc v6.13.1 2018-02-07 11:59:58 +00:00
9fb66186f0 Move to the correct coffeescript (no hyphen) dependency
Change-Type: patch
2018-02-07 11:20:49 +01:00
da8fe99ca4 Add typings for 'ent'
Change-Type: patch
2018-02-07 11:20:08 +01:00
20374fde36 Auto-merge for PR #777 via VersionBot
Add support for Balena in local ssh
2018-02-06 11:40:40 +00:00
5131f722a7 v6.13.0 2018-02-06 11:34:29 +00:00
1ef0a1028f Add support for Balena in local ssh
Change-Type: minor
2018-02-06 12:05:28 +01:00
0fd1f04eda Auto-merge for PR #781 via VersionBot
Switch back to upstream global-tunnel-ng
2018-02-05 19:07:08 +00:00
5c0ba5d06c v6.12.9 2018-02-05 19:00:36 +00:00
d9532b6fa0 Switch back to upstream global-tunnel-ng
Connects-To: #780

Change-Type: patch
2018-02-05 15:55:26 +00:00
b96065514f Auto-merge for PR #774 via VersionBot
Fix uuid params being parsed a numbers
2018-02-03 15:58:03 +00:00
0e9b8e4140 v6.12.8 2018-02-03 15:50:09 +00:00
d1c773360f Fix uuid params being parsed a numbers
Connects-To: #489
Change-Type: patch
2018-02-01 17:48:01 +02:00
74538bba8d Auto-merge for PR #767 via VersionBot
Add 'or mounted resinOS image'
2018-01-30 18:10:12 +00:00
64c95e3811 v6.12.7 2018-01-30 17:29:59 +00:00
33fd70291a Add 'or mounted resinOS image'
Connects-To: #764

Change-Type: patch
2018-01-30 17:30:18 +01:00
0cb4bc951a Auto-merge for PR #759 via VersionBot
Don't use the deprecated 'os configure' format in internal calls
2018-01-29 14:56:00 +00:00
3761ab9610 v6.12.6 2018-01-29 14:44:44 +00:00
8c29bba108 Don't use the deprecated 'os configure' format in internal calls
Change-Type: patch
2018-01-16 17:54:46 +01:00
4e41261237 Auto-merge for PR #754 via VersionBot
Fix breakage in deploy command
2018-01-11 11:32:17 +00:00
77529ef3b1 v6.12.5 2018-01-11 10:16:56 +00:00
0ba96adbbc Fix breakage in deploy command from recent TS conversion
Change-Type: patch
2018-01-11 10:33:08 +01:00
7df277c0bc Auto-merge for PR #753 via VersionBot
Add prettier
2018-01-10 10:18:18 +00:00
c94f7b10bd v6.12.4 2018-01-10 09:48:26 +00:00
83a76f7d6f Start using Prettier
Change-Type: patch
2018-01-10 09:23:00 +01:00
6c988241eb Move capitanodoc into the automation folder 2018-01-10 08:41:19 +01:00
29145dfc2d Auto-merge for PR #743 via VersionBot
Start seriously converting the CLI to TypeScript
2018-01-09 22:37:41 +00:00
4b74e8ec70 v6.12.3 2018-01-09 21:00:55 +00:00
612012aff8 Lots of small TypeScript tweaks & clarifications from review 2018-01-09 17:14:49 +01:00
6ab60d0ccd Avoid awkward multiline strings in doc generation code 2018-01-09 17:14:49 +01:00
6daed83d88 Lint TypeScript and CoffeeScript with resin-lint
Change-Type: patch
2018-01-09 17:14:49 +01:00
f25442c036 Move documentation generation to TypeScript
Change-Type: patch
2018-01-09 17:14:49 +01:00
ffffd447f2 Convert most of utils to TypeScript
Change-Type: patch
2018-01-09 17:14:48 +01:00
4b511c47f0 Start on some easy TS conversion 2018-01-09 17:14:48 +01:00
158d471a98 Auto-merge for PR #751 via VersionBot
Convert windows paths to posix when passing to tar
2018-01-09 15:33:28 +00:00
107a90395c v6.12.2 2018-01-09 14:03:45 +00:00
ce5fd53822 convert windows paths to posix when passing to tar
Due to https://github.com/mafintosh/tar-stream/issues/3, the tar module
needs posix style paths but system-specific paths are being supplied

Change-Type: patch
2018-01-08 22:50:11 +00:00
810ca78215 Auto-merge for PR #744 via VersionBot
Fix deprecation warning for os configure, when passing a bare UUID
2018-01-02 10:58:30 +00:00
eb945b3315 v6.12.1 2018-01-02 10:49:06 +00:00
34f24fe331 Fix deprecation warning for os configure, when passing a bare UUID
Change-Type: patch
2017-12-22 16:07:19 +01:00
743392017d Auto-merge for PR #737 via VersionBot
Add ssh option for direct host OS access
2017-12-19 08:45:56 +00:00
15b877f005 v6.12.0 2017-12-19 08:37:19 +00:00
0653769156 fixed example 2017-12-18 17:03:16 +01:00
3ed319872a refactored 2017-12-18 17:03:16 +01:00
ee124671d8 mention Resin OS version requirement 2017-12-18 17:03:16 +01:00
1b4dabd37c Add ssh option for direct host OS access
Use the `--host` (short `-H`) option in the ssh command to access
the host OS of the device.
Direct host OS is enabled for devices with Resin OS >= 2.7.5.

Change-Type: minor
Connects-To: #736
Signed-off-by: Andreas Fitzek <andreas@resin.io>
2017-12-18 17:03:16 +01:00
fdd253f042 Auto-merge for PR #729 via VersionBot
Standalone binary for Resin-CLI
2017-12-18 15:06:13 +00:00
1a15fdd2f0 v6.11.0 2017-12-18 14:48:09 +00:00
2c66280b3f Build standalone zips into a separate folder 2017-12-18 15:03:17 +01:00
778c39d947 Ensure MDNS service definitions are included in standalone binaries 2017-12-18 14:55:07 +01:00
fa15addfb2 Add standalone install instructions to the readme 2017-12-18 14:55:07 +01:00
afbb9474b7 Use proper strict settings for automation TS 2017-12-18 14:55:07 +01:00
0acb4f8cb1 Fix docs generation when building on windows
Change-Type: patch
2017-12-18 14:55:07 +01:00
08de0938a0 Autodeploy built standalone binaries for all platforms to github
Change-Type: minor
2017-12-18 14:55:07 +01:00
2c9b80c177 Add manual script to deploy built CLI binaries to GitHub 2017-12-18 14:55:07 +01:00
e8c19df8c9 Set up a script to automate builds, and support native extensions 2017-12-18 14:55:07 +01:00
7681003512 Package the CLI into a standalone runnable binary
This has no native modules yet, which means it works on Linux,
but ignoring any ext4 image data. Drivelist will fail for
some windows operations, but most other things should work.

This is only building a folder with a runnable binary, this needs
packaging before it can be distributable.

Change-Type: minor
2017-12-18 14:55:07 +01:00
dba8db19cb Move from open to opn
Change-Type: patch
2017-12-18 14:55:07 +01:00
d199cdf088 Auto-merge for PR #730 via VersionBot
Ensure logout works even with invalid credentials, or if not logged in
2017-12-15 17:52:38 +00:00
f2840c5ca4 v6.10.3 2017-12-15 17:48:25 +00:00
1c7a0ba4e1 Ensure logout works even with invalid credentials, or if not logged in
Before this point, if you had an invalid token, an expired token, or a
token for a different site, you couldn't log out to clear it properly.

Not a big deal, but awkward and messy, and easily fixed.

Change-Type: patch
2017-12-14 15:34:36 +01:00
41ff793372 Auto-merge for PR #721 via VersionBot
Inline the entire resin-cli-auth module
2017-11-27 17:28:43 +00:00
e4432d1a90 v6.10.2 2017-11-27 17:25:41 +00:00
bd6cb04a2b Replace underscore.string usage with lodash 2017-11-27 12:03:04 +02:00
001c8f9601 Inline the entire resin-cli-auth module
This is part of a general push to demodularize any code that isn't
realistically reusable outside resin-cli, to make the codebase easier to
manage and understand. Once this is done, we'll deprecate the original
module itself.

Change-Type: patch
2017-11-27 12:02:57 +02:00
f106b95be2 Auto-merge for PR #720 via VersionBot
Start adding TypeScript to the CLI and stop committing build output
2017-11-27 09:35:42 +00:00
6fbe602b77 v6.10.1 2017-11-27 09:32:58 +00:00
dc549a665b Update to latest latest typescript 2017-11-27 11:25:20 +02:00
46ca62db3e Include lib/ in published package too, to enable sourcemaps 2017-11-23 19:29:07 +02:00
eb68bb1a1a Set up TypeScript compilation, and make a small start on converting the CLI
Change-Type: patch
2017-11-23 19:28:15 +02:00
93d1e3a4a1 Rename gulp build step to coffee 2017-11-23 19:24:03 +02:00
ff2ee59dae Don't commit raw JS build output
This lets us avoid lots of potential conflicts, issues, and confusion,
and keeps reviews simpler and cleaner.

Change-Type: patch
2017-11-23 19:23:59 +02:00
6217b4a6b5 Auto-merge for PR #718 via VersionBot
Allow configuring images for both device and just applications
2017-11-17 10:46:33 +00:00
67fcc6791c v6.10.0 2017-11-17 10:43:54 +00:00
49d78c56fa Print a deprecation message if you use the old os configure format 2017-11-16 19:51:34 +01:00
e38a0c0047 Allow os configure to configure for an app, not just a specific device
This moves to --app and --uuid options, and deprecates the previous
format, but doesn't immediately remove it so this is not a breaking
change.

Connects-To: #691
Change-Type: minor
2017-11-16 19:51:34 +01:00
eef0d9cdbe Print help even for expected errors
Change-Type: patch
2017-11-16 19:51:17 +01:00
08c40195e5 Make sure everything uses the same shared deviceApiKey option 2017-11-16 19:09:20 +01:00
7e306fbce8 Auto-merge for PR #714 via VersionBot
Allow generating device configurating non-interactively
2017-11-16 14:13:08 +00:00
656656bec1 v6.9.0 2017-11-16 14:09:40 +00:00
87f46cb957 Allow non-interactice config generate for simple network settings
Fixes #695
Fixes #410
Change-Type: minor
2017-11-16 15:04:58 +01:00
f7075d7db9 Fix issue where network settings were not used by config generate
Change-Type: patch
2017-11-16 15:03:49 +01:00
2a2d621d6a Auto-merge for PR #717 via VersionBot
Remove resin promote command (which has never worked) to wait for larger resinOS provisioning updates
2017-11-16 13:59:18 +00:00
2db6cdd063 v6.8.3 2017-11-16 13:55:44 +00:00
1fafe64579 Remove resin promote command (which has never worked) to wait for larger resinOS provisioning updates
This would be a major change if the command was ever successful, but it
appears it hasn't ever worked for any available published version of
ResinOS, so it's not possible that there are users relying on it.

Change-Type: patch
2017-11-15 14:46:22 +01:00
6562eb544c Auto-merge for PR #713 via VersionBot
Fix 'cannot read property R_OK of undefined' error in Node >=6 <6.3
2017-11-14 10:30:30 +00:00
3763bf0712 v6.8.2 2017-11-14 10:27:31 +00:00
890a02e2c8 Fix 'cannot read property R_OK of undefined' error in Node >=6 <6.3
Change-Type: patch
2017-11-11 12:29:49 +01:00
427664c729 Auto-merge for PR #711 via VersionBot
Avoid AmbiguousApplication errors in device register when an id is used
2017-11-09 15:20:12 +00:00
727a245715 v6.8.1 2017-11-09 15:16:17 +00:00
a2635f47ee Avoid AmbiguousApplication errors in device register when an id is used
Change-Type: patch
Connects-To: #665
2017-11-09 16:03:06 +01:00
d316f67367 Auto-merge for PR #706 via VersionBot
Update resin-preload to 5.0.0 to handle jetson-tx2
2017-10-27 14:39:30 +00:00
ebd1d7e370 v6.8.0 2017-10-27 14:32:34 +00:00
eef192ff68 Allow preloading jetson-tx2 images, improve flasher images detection and remove the --dont-detect-flasher-type-images option.
* update resin-preload to 5.0.0

Connects-To: #705

Change-Type: minor
2017-10-27 12:14:27 +02:00
36d3c92006 Auto-merge for PR #702 via VersionBot
Add preload to the CLI docs
2017-10-25 12:25:50 +00:00
68e31468cc v6.7.4 2017-10-25 11:52:10 +00:00
dfd8b6717d Add preload to the CLI docs
Change-Type: patch
2017-10-25 13:17:07 +02:00
10d688c02d Auto-merge for PR #701 via VersionBot
Allow specifying `--commit=latest` for `resin preload`
2017-10-25 10:21:02 +00:00
737e961979 v6.7.3 2017-10-25 10:17:42 +00:00
3bca36c277 Allow specifying --commit=latest for resin preload
Connects-To: #700

Depends-On: https://github.com/resin-io/resin-preload/pull/137

Change-Type: patch
2017-10-25 12:09:08 +02:00
24b2c83e92 Auto-merge for PR #699 via VersionBot
Make update-notifier more resilient and ensure it obeys NO_UPDATE_NOTIFIER
2017-10-24 18:01:07 +00:00
266870018a v6.7.2 2017-10-24 17:57:39 +00:00
80bc044415 Make update-notifier more resilient and ensure it obeys NO_UPDATE_NOTIFIER, by updating it
Connects-To: #698
Change-Type: patch
2017-10-24 19:31:16 +02:00
563628a5a9 Auto-merge for PR #697 via VersionBot
Respect the --dont-check-device-type option, fix error message
2017-10-24 14:59:21 +00:00
385e2c7f7a v6.7.1 2017-10-24 14:56:48 +00:00
19ce4c4cdb Respect the -dont-check-device-type option, fix error message
Connects-To: #696

Change-Type: patch
2017-10-24 16:45:36 +02:00
0b26fda89c Auto-merge for PR #487 via VersionBot
Device api keys
2017-10-18 18:20:15 +00:00
3f692ecbb0 v6.7.0 2017-10-18 18:18:01 +00:00
2d43e47610 Add device api keys warning on device register and os configure 2017-10-18 13:43:16 +02:00
a8f1d16b26 Make resin os configure safe with device keys for all ResinOS versions 2017-10-18 13:43:16 +02:00
8e95757f47 Make resin config generate safe for all ResinOS versions 2017-10-18 13:43:16 +02:00
3fd4f328ab Added a device api key parameter to the os configure command.
Change-Type: minor
2017-10-18 13:43:16 +02:00
97eaf174ec Added a --device-api-key option to the config generate command.
Change-Type: minor
2017-10-18 13:43:16 +02:00
2ef56a9a3f Added a --device-api-key option to the device register command.
Change-Type: minor
2017-10-18 13:43:16 +02:00
93818b1a98 Auto-merge for PR #689 via VersionBot
Fix issue where `os download` would always download prod images
2017-10-18 10:46:19 +00:00
ce70102378 v6.6.13 2017-10-18 10:41:28 +00:00
0e4c6c459c Fix issue where os download would always download prod images
This also adds support for submodules using resin-sdk shared options
rather than resin-sdk-preconfigured.

Change-Type: patch
Connects-To: #688
2017-10-17 21:20:35 +02:00
6b96fe37ba Auto-merge for PR #687 via VersionBot
Update resin-preload to 4.0.2 to support preloading Edison images
2017-10-16 18:55:22 +00:00
e9c7e0e924 v6.6.12 2017-10-16 18:51:54 +00:00
119fa78927 Update resin-preload to 4.0.2 to support preloading Edison images
Change-Type: patch
2017-10-16 19:27:12 +02:00
dad655c9ec Auto-merge for PR #685 via VersionBot
Document how to `resin deploy` to an app as a collaborator
2017-10-13 14:29:14 +00:00
8af392029f v6.6.11 2017-10-13 14:26:20 +00:00
82888de036 Document how to resin deploy to an app as a collaborator
Change-Type: patch
2017-10-13 16:13:46 +02:00
b4a56e1541 Auto-merge for PR #676 via VersionBot
Ensure hostname truly is optional when configuring device images
2017-10-09 10:14:35 +00:00
19b0ec7f8b v6.6.10 2017-10-09 10:12:22 +00:00
3df7bfe700 Ensure hostname truly is optional when configuring device images
Change-Type: patch
2017-10-09 12:03:31 +02:00
d1fd5a6bff Auto-merge for PR #678 via VersionBot
Fix resin preload --splash-image argument handling
2017-10-06 09:22:01 +00:00
43a7e3ddf4 v6.6.9 2017-10-06 09:19:54 +00:00
10976bed43 Fix resin preload --splash-image argument handling
Connects-To: #677

Change-Type: patch
2017-10-06 11:16:06 +02:00
c187c113d9 Auto-merge for PR #675 via VersionBot
Ensure whoami failures (i.e. present but broken tokens) at startup don't break commands
2017-10-06 07:45:00 +00:00
f7c0258145 v6.6.8 2017-10-06 07:42:39 +00:00
eb729d149e Ensure analytics failures (e.g. from broken tokens) at startup don't break commands
Change-Type: patch
2017-10-05 19:03:01 +02:00
39ac28d4ef Auto-merge for PR #666 via VersionBot
Add windows instructions to fix node-gyp installs
2017-09-22 17:17:51 +00:00
989877d541 v6.6.7 2017-09-22 17:14:37 +00:00
3f3af216fd Add instructions for an admin console 2017-09-22 20:10:50 +03:00
9aef632afd Update to resin-sync, which fixes local push on windows
Change-Type: patch
2017-09-22 19:17:28 +03:00
ef6e00bcea Add windows instructions to fix node-gyp installs
Change-Type: patch
2017-09-22 16:27:24 +03:00
55a7dccc74 Merge pull request #659 from resin-io/catch-uncommitted
Move to using the catch-uncommitted npm package
2017-09-15 19:45:45 +02:00
62035fac83 Move to using the catch-uncommitted npm package 2017-09-15 14:36:33 +02:00
950201973b Auto-merge for PR #655 via VersionBot
Create ISSUE_TEMPLATE.md
2017-09-11 14:53:51 +00:00
e431083e84 v6.6.6 2017-09-11 14:45:51 +00:00
1ff9cf02d7 Create ISSUE_TEMPLATE.md
Fixes #529
Change-Type: patch
2017-09-04 20:18:23 +03:00
bd3b9e32a9 Auto-merge for PR #643 via VersionBot
Fix lodash bugs in device move & quickstart
2017-08-31 12:30:48 +00:00
5a620d6c9e v6.6.5 2017-08-31 12:28:28 +00:00
492e35e5c2 Fix lodash bugs in device move & quickstart
Change-Type: patch
2017-08-31 20:22:36 +08:00
8980bc704a Auto-merge for PR #644 via VersionBot
Check for uncommitted output
2017-08-31 12:21:32 +00:00
8b9e78d645 v6.6.4 2017-08-31 12:18:41 +00:00
8f0131cf50 Tweak catch-uncommitted-output script for clarity, after review 2017-08-31 20:12:06 +08:00
47407a84fb Use only npm 4, for now 2017-08-31 20:12:06 +08:00
d858f3fd90 Make sure the catch-uncommitted-output script fails if the build fails 2017-08-31 20:12:06 +08:00
a36f765f1b Catch uncommitted build output automatically in Travis
Connects-To: #573
Change-Type: patch
2017-08-31 20:12:06 +08:00
fd308a5131 Auto-merge for PR #641 via VersionBot
Update README to link to the full CLI command documentation
2017-08-31 11:47:53 +00:00
3052100973 v6.6.3 2017-08-31 11:44:39 +00:00
cfdd4d3d69 Update README to link to the full CLI command documentation
Also update description to match package.json & repo, and remove
quickstart (since it's unstable at the moment).

Change-Type: patch
2017-08-31 19:37:19 +08:00
0a3123a9cf Auto-merge for PR #642 via VersionBot
Use DOCKER_HOST from env if possible, and no connection options are available
2017-08-31 07:24:30 +00:00
5474666f9e v6.6.2 2017-08-31 07:22:43 +00:00
2bbd45e867 Use DOCKER_HOST from env if possible, and no connection options are available
Connects-to: #625
Change-Type: patch
2017-08-31 15:18:32 +08:00
e8c4a9abfd Auto-merge for PR #650 via VersionBot
Update resin-preload to 3.1.4
2017-08-28 09:38:08 +00:00
710a938b3f v6.6.1 2017-08-28 09:34:59 +00:00
be7c1d278e Update resin-preload to 3.1.4
Connects-To: #649

Change-Type: patch
2017-08-28 10:49:08 +02:00
dfcac4a532 Auto-merge for PR #647 via VersionBot
Add a --dont-check-device-type option for `resin preload`
2017-08-28 08:45:41 +00:00
a5128cd49e v6.6.0 2017-08-28 08:42:44 +00:00
223432406d Add a --dont-check-device-type option for resin preload
Connects-To: #646

Change-Type: minor
2017-08-25 12:27:38 +02:00
cafde01886 Auto-merge for PR #645 via VersionBot
Remove resin-preload build filtering workaround.
2017-08-24 12:28:34 +00:00
0158d1da48 v6.5.3 2017-08-24 12:25:11 +00:00
e0d661a1da Remove resin-preload build filtering workaround.
Connects to #640

Change-Type: patch
2017-08-24 12:39:17 +02:00
b152482133 Reformat changelog for versionbot 2017-08-22 19:30:34 +02:00
4cdf3acf42 v6.5.2 2017-08-22 19:22:05 +02:00
314fcd3919 Merge pull request #639 from resin-io/628-add-progress-bars-for-resin-preload
Add progress bars and spinners for resin preload.
2017-08-22 18:44:56 +02:00
b07a394592 Add progress bars and spinners for resin preload.
Connects-To: #628

Change-Type: minor
2017-08-22 18:42:20 +02:00
14c5d938a6 Merge pull request #627 from resin-io/626-dont-preload-the-same-image-twice
Don't try preloading the same build twice in an image.
2017-08-21 19:26:53 +02:00
c6c2f0bedc Don't try preloading the same build twice in an image.
Connects to #626

Change-Type: patch
2017-08-21 19:24:30 +02:00
6882f4bbe8 Fix missed changelog link 2017-08-21 11:37:23 +02:00
74d6cfb8d2 v6.5.1 2017-08-21 11:33:12 +02:00
3b30a7c4b0 Merge pull request #638 from resin-io/636-replace-lodash-object-with-fromPairs
Use _.fromPairs instead of _.object which was removed in lodash 4.0.0
2017-08-21 10:13:05 +02:00
115e46573b Use _.fromPairs instead of _.object which was removed in lodash 4.0.0
Connects-To: #636

Change-Type: patch
2017-08-21 10:01:25 +02:00
2ad789457f Merge pull request #635 from resin-io/prepublishOnly
Move to prepublishOnly to speed up npm install a little
2017-08-18 17:37:46 +02:00
9beb6de7d8 Move to prepublishOnly to speed up npm install a little 2017-08-18 15:56:35 +02:00
74743745c4 v6.5.0 2017-08-18 15:45:45 +02:00
6fae5a2dd9 Merge pull request #631 from resin-io/autodeploy
Set up Travis npm autopublish
2017-08-18 15:40:18 +02:00
4320f33d8e Set up Travis npm autodeploy 2017-08-18 12:41:30 +02:00
435fedfa07 Merge pull request #630 from resin-io/629-fix-resin-preload-when-a-commit-is-provided
Fix resin preload when a commit is provided.
2017-08-18 09:38:40 +02:00
999f269e36 Fix resin preload when a commit is provided.
Connects-To #629

Change-Type: patch
2017-08-18 09:37:37 +02:00
224dfce4a8 Merge pull request #624 from resin-io/remove-babel
Remove unused babel dependency
2017-08-17 11:40:15 +02:00
60634a5ebd Merge pull request #623 from resin-io/upgrade-lodash
Upgrade to lodash v4
2017-08-17 11:38:56 +02:00
f8f1f52662 Remove unused babel dependency 2017-08-16 19:41:56 +02:00
e204707ee0 Upgrade to lodash v4 2017-08-16 18:58:46 +02:00
b28a4a5f99 Merge pull request #621 from resin-io/update-package-description
Update npm package description
2017-08-16 12:45:09 +02:00
340b2d5572 Update npm package description 2017-08-16 11:47:43 +02:00
4dec846256 Merge pull request #617 from resin-io/594-shared-app-lookup
Allow the looking up of applications with the owner username
2017-08-14 22:43:56 +01:00
dc1b3c3239 Allow the looking up of applications with the owner
change-type: minor
Connects-to: #594
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-08-14 17:00:33 +01:00
2fecb8d3e9 Merge pull request #619 from resin-io/618-use-patched-global-tunnel-ng
Use forked global-scoket-ng that doesn't proxy connection to socket files.
2017-08-11 17:18:52 +02:00
4665a72baf Use forked global-scoket-ng that doesn't proxy connection to socket files.
Connects to #618

Change-Type: patch
2017-08-11 17:02:17 +02:00
b208d601c9 Merge pull request #610 from resin-io/609-integrate-docker-preload
Integrate resin-preload
2017-08-11 15:45:05 +02:00
22b3c39b2b Integrate resin-preload
* split docker connection options from lib.utils.docker.appendOptions

Connects to #609
Connects to https://github.com/resin-io/resin-preload/pull/81

Change-Type: minor
2017-08-11 15:43:07 +02:00
30cca93283 v6.4.0 2017-08-11 13:31:53 +02:00
f0d9b615a7 Merge pull request #615 from resin-io/612-remove-inconsistent-tagging-message
Remove inconsistent (and now unneccesary) 'Tagging image as' message
2017-08-11 13:31:16 +02:00
b7e2c2571f Remove inconsistent (and now unneccesary) 'Tagging image as' message
Change-Type: patch
2017-08-11 13:30:44 +02:00
25538a9afb Merge pull request #616 from resin-io/no-docker-warning
Provide a helpful warning when Docker is not installed
2017-08-11 11:34:04 +02:00
5daa682183 Provide a helpful warning when Docker is not installed
Change-Type: patch
2017-08-10 21:40:05 +02:00
bad4493867 Merge pull request #614 from resin-io/603-node-upgrade-help
Help users using old node versions to upgrade
2017-08-10 14:43:29 +02:00
9e6dd57a5c Help users using old node versions to upgrade 2017-08-09 15:20:50 +02:00
1b86741fa2 v6.3.1 2017-08-08 11:37:22 +03:00
6cca43a09e v6.3.0 2017-08-03 18:05:27 +03:00
b622995a5a Merge pull request #606 from resin-io/534-update-resin-sync
update resin-sync
2017-08-03 18:04:13 +03:00
4329857a16 update resin-sync 2017-08-03 18:01:45 +03:00
d803cfab3a v6.2.0 2017-07-27 14:07:05 +03:00
e7d7ca807f Merge pull request #601 from resin-io/597-resin-wifi-config
Support the new OS with resin-sample.ignore connection file
2017-07-27 14:02:29 +03:00
22e0b4b9dc Support the new OS with resin-sample.ignore connection file 2017-07-27 14:01:49 +03:00
759baf3eda v6.1.1 2017-07-18 18:09:32 +02:00
e6dc7f8075 Merge pull request #595 from resin-io/simplify-login-message
Hide the intro quickstart message, for now (until it gets renovated)
2017-07-18 17:02:18 +02:00
1f0bec39d9 Hide the intro quickstart message for now (until it gets renovated) 2017-07-18 16:57:16 +02:00
64b6549fde v6.1.0 2017-06-30 20:49:08 +02:00
fc7e08c886 Merge pull request #586 from resin-io/543-fix-cwd-string
Upgrade resin-sync to fix node 8 bug
2017-06-30 20:45:59 +02:00
4aadfe9326 Upgrade resin-sync to fix node 8 bug
Connects-To: #543
Change-Type: patch
2017-06-30 19:49:35 +02:00
a65868cbbf Tiny patch to move a CHANGELOG line to the right place 2017-06-29 12:44:14 +02:00
8ab528aae4 Merge pull request #570 from resin-io/bump-resin-sync
Bump resin-sync@8.0.0
2017-06-29 11:56:57 +03:00
d93b82a269 Bump resin-sync@8.0.0
- resin sync: do not explicitly disable ControlMaster SSH option
- resin sync: whitelist collaborators

fixes #422
change-type: minor
2017-06-29 11:56:01 +03:00
3095938b0e Merge pull request #569 from resin-io/permit-ssh-controlmaster
Do not explicitly disable ControlMaster option for device SSH connections
2017-06-29 11:54:19 +03:00
91b3442fc9 Do not explicitly disable ControlMaster option for device SSH connections
The backend server that handles `resin ssh` now supports it.

Also removed the option from local ssh connections to devices, where it
basically has no effect (dropbear on devices supports it)

change-type: minor
fixes #568
2017-06-29 11:53:31 +03:00
e660c6ae90 Merge pull request #557 from resin-io/539-fix-clearLine
Fix issue when using resin deploy with non-standard stdin
2017-06-28 20:30:48 +02:00
e2a165ce80 Show a correct [Info] tag with the deploying progress bar 2017-06-28 18:57:49 +02:00
ce5685551d Remove unused function 2017-06-28 18:52:44 +02:00
15e677e9f1 Refactor stream logger to keep streams as state 2017-06-28 18:52:40 +02:00
5ccde3db8e Fix issue when using resin deploy with non-standard stdin
This fixes issues when piping `resin deploy` to a non-TTY, and should
solve issues on Windows too.

Connects-To: #539
2017-06-28 18:40:00 +02:00
cb550f65bb Merge pull request #582 from resin-io/580-fix-emulated-build-arg
Fix issue where emulated builds broke Docker ARG commands
2017-06-27 16:07:05 +02:00
8d3987fc70 Fix issue where emulated builds broke Docker ARG commands 2017-06-27 15:06:06 +02:00
4fa8d86f02 v6.0.0 2017-06-26 13:38:22 +02:00
6b22166887 Merge pull request #577 from jacintoArias/master
Allow --squash option on resin build command
2017-06-26 13:35:35 +02:00
9e555b3dba Updated CHANGELOG.md 2017-06-26 13:34:22 +02:00
bea7b2035a Added --squash option to resin build command 2017-06-26 13:33:50 +02:00
abebf6b4f4 Merge pull request #576 from resin-io/567-require-node-6
Remove Buffer polyfill, require Node v6+, and print warnings in older versions
2017-06-26 13:28:20 +02:00
6182e7c98a Make Node warning a multi-line string 2017-06-26 13:24:54 +02:00
410390a9ae Remove Buffer polyfill, require Node v6+, and print warnings in older versions 2017-06-22 18:59:50 +02:00
11079caf26 v5.11.1 2017-06-22 18:19:53 +02:00
0c6545218a Merge pull request #574 from resin-io/565-log-node-version
Include node version in sentry logs
2017-06-22 18:17:33 +02:00
7fc806c75f Merge pull request #571 from resin-io/566-fix-v4-compat
Add a polyfill to fix local configure in older (<6) Node versions
2017-06-22 18:12:39 +02:00
18533de3da Include node version in sentry logs 2017-06-22 14:48:46 +02:00
c5afe5f2c2 Update build output, after missed recompile 2017-06-22 14:10:16 +02:00
2875bd672e Add a polyfill to fix local configure in older (<6) Node versions 2017-06-22 13:38:38 +02:00
26d123b33d Merge pull request #563 from resin-io/562-fix-tls-connect
Read ca files and convert to string before passing to the docker daemon
2017-06-22 08:41:10 +01:00
5000febf72 Read ca files and convert to string before passing to the docker daemon
Before this commit, the docker daemon would recieve the filename of the
.pem files, which would be interpreted as the body and would fail. This
commit ensures that the actual body of the pem files are sent to the
daemon.

Change-type: patch
Connects-to: #562
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-06-21 11:11:24 +01:00
378f894da3 v5.11.0 2017-06-19 01:16:04 +03:00
c891d690ec update lockfile 2017-06-19 01:14:08 +03:00
8efaec63ff Merge pull request #559 from resin-io/558-absolute-qemu
Make emulated builds reliable in the presence for WORKDIR comands
2017-06-15 19:41:52 +02:00
b756f2a597 Make emulated builds reliable in the presence for WORKDIR comands
Connects-To: #558
Change-Type: patch
2017-06-15 16:36:44 +02:00
c986e142e2 Merge pull request #548 from resin-io/462-noninteractive-device-init
non-interactive device init
2017-06-15 17:06:41 +03:00
8bdacbb11e dedupe the shared options 2017-06-15 16:43:12 +03:00
d2a9aee685 improve available drives listing
make the config a path to the file and not the stringified JSON
2017-06-15 16:43:02 +03:00
77a4c6fdc2 move available-drives command to util 2017-06-15 16:43:02 +03:00
4d935d62fc improve wording 2017-06-15 16:42:46 +03:00
e8b44d7250 Forced update to the newest resin-sdk 2017-06-15 16:40:46 +03:00
5c5cfde49f automated init doc 2017-06-15 16:39:55 +03:00
97480d3aa4 Improve the supported device types listing 2017-06-15 16:39:55 +03:00
4ac8cb1003 rebuild docs 2017-06-15 16:39:55 +03:00
2e7e033bb9 allow prebuilding the device config and reusing it 2017-06-15 16:39:55 +03:00
9fb5b52069 update dependencies 2017-06-15 16:39:55 +03:00
ad940824a6 list detected drives with resin os available-drives 2017-06-15 16:39:55 +03:00
ed83514a2f allow passing --drive to resin device init 2017-06-15 16:39:55 +03:00
8d91a5732a tolerate the --yes param to device init 2017-06-15 16:39:55 +03:00
1cfe64e4a7 choose version during device init, and list versions with resin os versions 2017-06-15 16:39:55 +03:00
24388811ad remove the user requirement for the supported devices list 2017-06-15 16:38:55 +03:00
f465e74a87 Merge pull request #556 from resin-io/deploy-ux-improvements
Resin deploy ux improvements
2017-06-15 14:31:27 +02:00
c8d51d92e7 Show a clear message immediately as the deploy starts, if we're deploying an image.
Change-Type: patch
2017-06-15 12:36:40 +02:00
5a28d4c92f Make cleanup reliable if a local build fails 2017-06-15 12:36:40 +02:00
66a4faeea5 Small improvement to resin deploy docs
Change-Type: patch
2017-06-15 12:36:40 +02:00
2fce0e964b Merge pull request #554 from resin-io/549-gzip-image-deploy
Gzip images when deploying
2017-06-15 12:35:54 +02:00
a29b40eefa Move promise.spread to promise.join for clarity (from review) 2017-06-15 12:34:26 +02:00
cf7bf2cb7d Fix the gzip level for image uploads to a good perf/size balance 2017-06-15 12:01:22 +02:00
df3c5ca07f Gzip while streaming, rather than gzipping the buffer up front
Connects-To: #549
2017-06-15 12:01:22 +02:00
e584dc43f7 Gzip images when uploading in resin deploy
Change-Type: minor
Connects-To: #549
2017-06-15 12:01:22 +02:00
90a5b15dbc Refactor docker stream buffering before start gzipping 2017-06-15 12:01:22 +02:00
390332cd6c Merge pull request #555 from resin-io/553-fix-qemu-path
Ensure emulated builds use the correct relative path to qemu
2017-06-15 12:00:58 +02:00
37ec11bf25 Ensure emulated builds use the correct relative path to qemu
Change-Type: patch
Connects-To: #553
2017-06-14 20:50:24 +02:00
08ce1abd20 Merge pull request #511 from resin-io/emulated-builds
Add emulated build option to resin build
2017-06-14 11:06:34 +01:00
f2862f7fe2 Add emulated build option to resin build
This commit adds the ability to run a Docker build for an architecture
which is not the host architecture, using qemu-linux-user. Currently
this is only supported for linux.

Added:
* Installation of qemu which supports propagated execve flags
* Copying of qemu binary into the build context
* Transposing the given Dockerfile to use the qemu binary
* Intercepting of the build stream, so the output looks *almost* exactly
  the same.

Change-type: minor
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-06-14 12:06:03 +01:00
e0673c98fc Remove leftover action/util build output 2017-06-09 03:24:34 +02:00
4ab67ed71d changelog 2017-06-08 13:53:41 +03:00
5ea263ef2e Add package-lock.json 2017-06-08 13:52:47 +03:00
31419b399e v5.10.2 2017-05-31 13:08:04 +01:00
9e8b09010f Merge pull request #540 from resin-io/args
resin build: fix mismatch in command line argument signature
2017-05-31 13:05:48 +01:00
974be5cc13 resin build: fix mismatch in command line argument signature
The command line arg was taking `devicetype`, but the rest of the code
uses `deviceType`. Thus it was impossible to specify a device type
in practice to build a `Dockerfile.template`.

Change-type: patch
Signed-off-by: Gergely Imreh <imrehg@gmail.com>
2017-05-30 10:34:32 +01:00
ec386b807f Merge pull request #518 from resin-io/485-update-interval
Change update check interval to once a day
2017-05-24 11:12:54 -07:00
abc183a729 Change update check interval to once a day
This gives the user enough notice to stay well updated, but won't spam
them if they're using resin-cli frequently.

Connects-to: #485
Change-type: patch
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-05-23 17:29:23 -07:00
e5ed6fab85 v5.10.1 2017-05-22 21:59:12 +02:00
eb9152255e Merge pull request #536 from resin-io/535-fix-local-ssh
Fix breaking bug in resin local ssh
2017-05-22 21:55:59 +02:00
78ab47b584 Fix breaking bug in resin local ssh 2017-05-22 20:49:34 +02:00
5b651c7821 v5.10.0 2017-05-22 15:44:28 +02:00
95fc5d7785 Merge pull request #517 from resin-io/ssh-proxy
resin ssh proxy support
2017-05-22 13:04:49 +02:00
4d18e92686 changelog 2017-05-20 00:20:22 +03:00
2d729a82a0 fix valid-email path 2017-05-20 00:19:31 +03:00
4b5240d8cd check for proxytunnel presence 2017-05-19 02:10:14 +03:00
6ae59654a0 fix 2017-05-19 01:44:30 +03:00
b88f7a993c escape params 2017-05-19 01:25:01 +03:00
880fb43fd9 some fixes 2017-05-18 15:12:52 +03:00
fa71df7c70 use upstream dependency 2017-05-12 18:09:43 +03:00
bc79832e1d resin ssh proxy support 2017-05-12 18:09:43 +03:00
1d06bc1b4f Merge pull request #524 from resin-io/520-include-command-line
Include full command line arguments in Sentry errors
2017-05-12 13:27:53 +02:00
88d5ec0c94 Merge pull request #525 from resin-io/508-fix-docs
Get docs and the code generating them back in sync
2017-05-12 13:27:35 +02:00
ebd9e92b10 Merge pull request #523 from resin-io/522-username-in-errors
Include the username used in Sentry errors
2017-05-12 12:04:51 +02:00
a5b535753f Include the username used in Sentry errors, to help us debug them 2017-05-12 12:02:26 +02:00
6e5e4bd8a6 Get docs and the code generating them back in sync (from #515, #508) 2017-05-12 12:01:37 +02:00
6e034acf23 Include full command line arguments in Sentry errors, to help us debug them 2017-05-12 11:48:43 +02:00
3a44782c38 Merge pull request #515 from resin-io/build-time-vars
Add ability to specify built-time variables for local build
2017-05-11 14:31:45 +03:00
654ec75598 Renamed build arg option to —buildArg/-B 2017-05-11 14:20:24 +03:00
66876a2c85 Add ability to specify built-time variables for local build
Change-Type: patch
2017-05-11 14:14:43 +03:00
8cb862359b Merge pull request #514 from resin-io/upload-progress-bar
Add progress bar for upload progress
2017-05-11 14:12:21 +03:00
172fa37bd4 Added CHANGELOG entry 2017-05-10 23:06:35 +03:00
fc5640c79d Draw a progress bar for upload progress
Change-Type: patch
2017-05-10 22:28:51 +03:00
db9225f00a Merge pull request #398 from resin-io/395-show-device-dashboard-link
Show device dashboard url in 'resin device' output
2017-05-08 14:05:40 +03:00
c12b59b978 Show device dashboard url in 'resin device' output
change-type: minor
2017-05-08 13:51:06 +03:00
7eeafa935a Merge pull request #510 from resin-io/highlight-cache-usage
Highlight cache usage in resin build
2017-05-04 14:12:40 +01:00
404348f92e Highlight cache usage in resin build
This commit will highlight the usage of the cache when doing a docker
build via `resin build`, which not only helps the user understand what
the build is doing, but also achieves more parity with the cloud
builder.

Change-type: patch
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-05-04 14:09:23 +01:00
4d2af251b2 Merge pull request #502 from resin-io/not-enough-unicorns
Fix the not-enough-unicorns bug
2017-05-04 13:22:42 +01:00
d249ac168a Fix the not-enough-unicorns bug
Add successful build indicator in the form of a unicorn.

Change-type: patch
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-05-04 13:18:25 +01:00
65eaad2ed5 Merge pull request #493 from resin-io/local-upload-logs
Upload logs when doing both build + deploy
2017-05-04 13:16:01 +01:00
3ff5880ae3 Allow resin-cli deploy to also upload build logs if present
If build is ran through `resin deploy`, then logs will be stored and
uploaded to the database, where the dashboard can display them

Change-type: minor
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-05-04 13:11:50 +01:00
511d2abe1d Merge pull request #508 from resin-io/507-add-device-types-note
Add note on how to get resin device types
2017-05-02 10:37:30 +01:00
029b7c7164 Add note on how to get resin device types
Connects to #507

Change-Type: patch
2017-05-01 17:14:58 +01:00
afafa22694 v5.9.1 2017-05-01 14:19:21 +03:00
2df4422748 v5.9.0 2017-05-01 14:14:37 +03:00
e099f0b8b3 Merge pull request #505 from resin-io/proxy
proxy support
2017-05-01 14:12:49 +03:00
8866f47805 proxy support 2017-05-01 14:00:50 +03:00
1d8382e91d v5.8.1 2017-04-27 16:24:57 +03:00
110e18bc88 Merge pull request #500 from resin-io/fix-ssh
fix ssh proxy URL retrieval
2017-04-27 16:24:08 +03:00
3df30c8b5a fix ssh proxy URL retrieval 2017-04-27 16:20:32 +03:00
ebfd842ae5 Merge pull request #499 from resin-io/add-local-build-docs
Add documentation for new resin build and resin deploy commands
2017-04-26 14:15:34 +01:00
39b171fd2a Add documentation for new resin build and resin deploy commands
Change-type: patch
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-04-26 13:38:35 +01:00
04c2333a54 v5.8.0 2017-04-26 12:20:30 +01:00
65884c81a4 Merge pull request #498 from resin-io/prettify-build
Add cloud-builder builder output parity to build and deploy
2017-04-25 13:37:26 +01:00
f50ae65560 Add cloud-builder builder output parity to build and deploy
Change-type: minor
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-04-25 13:13:11 +01:00
be0d6d5a99 Merge pull request #497 from resin-io/share-build-opts
Share nocache and tag build options between build and deploy
2017-04-24 20:56:41 +01:00
4fa1a9c1c6 Share nocache and tag build options between build and deploy
`resin build` had access to the `--nocache` and `--tag` options for
building with docker, but `resin deploy` did not. This commit adds the
options to the shared dockerUtils.appendOptions function.

Change-type: patch
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-04-24 20:05:18 +01:00
284030d83d Merge pull request #495 from resin-io/fix-build-source
Respect source parameter in resin build
2017-04-24 20:04:22 +01:00
9050cb1975 Respect source parameter in resin build
Upon changing the name of the source parameter from `context`, some
places weren't changed, this commit fixes that.

Change-type: patch
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-04-24 16:41:15 +01:00
d55ce853a7 Merge pull request #466 from resin-io/local-build
Add ability to build and deploy image locally using resin-cli
2017-04-24 10:39:46 +01:00
d3772386bf Add ability to build and deploy image locally using resin-cli
Using `resin build` a user can now build an image on their own docker
daemon. The daemon can be accessed via a local socket, a remote host and
a remote host over a TLS socket. Project type resolution is supported.
Nocache and tagging of images is also supported.

Using `resin deploy` a user can now deploy an image to their fleet. The
image can either be built by `resin-cli`, plain Docker, or from a remote
source.

Change-type: minor
Signed-off-by: Cameron Diver <cameron@resin.io>
2017-04-23 14:31:45 +01:00
2e042499af Merge pull request #490 from resin-io/486-capture-init-errors
log unhandled exceptions
2017-04-21 23:11:56 +03:00
225d3acf9e log unhandled exceptions 2017-04-21 13:10:30 +03:00
75d10286ad v5.7.2 2017-04-18 12:15:11 +03:00
d6e0616c58 Merge pull request #482 from resin-io/node-v4-support
update to modules that have native node v4 support
2017-04-18 12:12:52 +03:00
380a94f0f8 update to modules that have native node v4 support 2017-04-14 14:48:19 +03:00
9da8c5f09a ignore .DS_Store 2017-04-14 11:41:19 +03:00
89b88315e4 Merge pull request #480 from resin-io/479-resin-local-docs
Add resin local docs
2017-04-11 16:15:30 +01:00
11e8ca178c Add resin local docs
Connects to #479

Change-Type: patch
2017-04-11 14:25:54 +01:00
e583e6f04c Merge pull request #476 from resin-io/fix-peer-dep-error
Move to a consistent resin-token by upgrading to new v6 SDK and resin-cli-auth
2017-04-10 12:42:06 +02:00
0cce2a7ab7 Move to a consistent resin-token by upgrading to new v6 SDK and resin-cli-auth 2017-04-07 21:26:56 +02:00
965aa7e4d4 v5.7.1 2017-04-03 16:07:32 +02:00
0ca64b18a2 Merge pull request #471 from resin-io/470-non-semver-os
Handle non-semver OS versions
2017-04-03 16:06:07 +02:00
63e1313f44 Handle non-semver OS versions 2017-04-03 15:36:04 +02:00
96a7a24738 v5.7.0 2017-03-28 14:43:31 +03:00
dede4bb329 Merge pull request #461 from resin-io/452-fix-permissions
isolate the sudo-runnable command
2017-03-28 14:40:51 +03:00
77b30409bb update resin-device-init to pull in etcher-image-write 2017-03-28 14:37:13 +03:00
137473353c remove username request from the signup process 2017-03-28 14:37:11 +03:00
08b3db717e use individual methods promisification instead of promisifyAll 2017-03-27 12:14:55 +03:00
6cf32e445a isolate the sudo-runnable command 2017-03-27 11:43:35 +03:00
857c5204b9 v5.6.1 2017-03-23 15:52:06 +03:00
169609620a Merge pull request #459 from resin-io/fix-unneeded-warning
Fix unneeded warning
2017-03-23 15:51:13 +03:00
8149172eb0 changelog 2017-03-23 15:32:06 +03:00
cba105a41b suppress warning during the device init OS download 2017-03-23 15:27:54 +03:00
69dff0c603 Merge pull request #450 from resin-io/420-add-sentry
Add sentry error tracking
2017-03-23 13:11:15 +01:00
6ffa4070ff Merge pull request #457 from resin-io/replace-gitter-with-forums
Point to forums instead of gitter
2017-03-23 14:10:18 +02:00
f05b04a6a1 Move sentry DSN to config 2017-03-23 13:10:15 +01:00
88d8112402 Add sentry error tracking 2017-03-23 12:59:42 +01:00
f940d7428c Point to forums instead of gitter
change-type: patch
2017-03-23 12:19:59 +02:00
697779868e Merge pull request #437 from alisondavis17/patch-1
Update README.md
2017-03-23 12:19:24 +02:00
d90874dbef v5.6.0 2017-03-23 01:36:08 +03:00
65b2347b57 Merge pull request #453 from resin-io/41-select-os-version
Closes #451
2017-03-23 01:32:15 +03:00
b25034978b use the published depdendencies 2017-03-23 01:24:25 +03:00
a817bb2135 update gitignore 2017-03-22 22:47:26 +03:00
b629c3601e implement the version menu 2017-03-22 15:45:05 +03:00
3619b2f117 allow specifying the version 2017-03-22 13:28:46 +03:00
4231f50c4c download the non-preview version by default 2017-03-22 12:55:55 +03:00
95fff4b7c4 build bare modules 2017-03-22 12:46:06 +03:00
b3aa3d35f7 fix resin local push help message and lint errors 2017-03-21 12:06:05 +03:00
9f3108c5e7 5.5.0 2017-03-10 18:19:45 +00:00
c95a01e34e Merge pull request #444 from resin-io/scan-sudo-and-fixed-latency
Scan sudo and fixed latency
2017-03-10 18:04:56 +00:00
19c51929a9 Generate JS 2017-03-10 17:59:54 +00:00
73dd625ede Require superuser for scan commands, also introduce docker timeout 2017-03-10 17:59:54 +00:00
08db3ace03 Bump resin-sync@7.0.0: use experimental rds which requires superuser permissions 2017-03-10 17:59:54 +00:00
2125cf9649 5.4.0 2017-03-09 23:56:10 +00:00
e5a7fa5617 Merge pull request #439 from resin-io/resin-local-stop
Resin local stop
2017-03-09 23:50:58 +00:00
3324ff4dee Generate JS 2017-03-09 23:49:59 +00:00
7ad468dc54 Implement 'resin local stop' 2017-03-09 23:49:56 +00:00
8474ee726c Update README.md
Change Support section to point to Forums instead of Gitter
2017-03-09 14:46:20 -08:00
29b2f4afa8 Merge pull request #433 from resin-io/filter-missing-dockerinfo-devices
Filter our devices that do not expose docker socket
2017-03-09 21:19:23 +00:00
7aee4d6d7f Filter our devices that do not expose docker socket 2017-03-09 20:54:11 +00:00
7af004501a Merge pull request #430 from resin-io/rdt-merge
Rdt merge
2017-03-09 11:21:25 +00:00
2d09c18d6b Build JS 2017-03-08 23:41:55 +00:00
53bf314820 Remove app create from primary commands 2017-03-08 23:41:35 +00:00
1ae1a15259 Implement 'resin local' 2017-03-08 23:41:35 +00:00
20ed8c9169 Implement 'resin local push' 2017-03-08 23:41:32 +00:00
977e3fb0ff Implement 'resin local ssh' 2017-03-08 23:41:29 +00:00
c5df32f952 Implement 'resin local scan' 2017-03-08 22:43:23 +00:00
f5cd3375f2 Implement 'resin local promote' 2017-03-08 22:43:23 +00:00
3b4c8f2a01 Implement 'resin local logs' 2017-03-08 22:43:23 +00:00
356042557e Implement 'resin local flash' 2017-03-08 22:43:21 +00:00
00753a5776 Implement 'resin local configure' 2017-03-08 18:43:34 +00:00
eea9a2f723 5.3.0 2017-03-03 18:14:48 +02:00
5ebdf4a8ac Merge pull request #424 from resin-io/ssh-handle-undefined-uuid
resin ssh: handle undefined uuid parameter
2017-03-03 17:29:21 +02:00
fb06249b08 resin ssh: handle undefined uuid parameter
change-type: patch
2017-03-03 17:25:51 +02:00
9214cb0d90 Merge pull request #416 from resin-io/support-aufs-resin-sync
Support aufs resin sync
2017-02-21 15:03:59 +02:00
a04c3b9c7b Generate JS and add gulpfile option to suppress diff 2017-02-21 14:45:47 +02:00
2fde6241c2 Support resin sync for remote resin.io AUFS devices
change-type: minor
2017-02-01 15:52:17 +02:00
ffa645f85c Merge pull request #413 from resin-io/sdk-browser
Use the updated SDK
2017-01-27 13:41:17 +01:00
b629ee6164 Move to preconfigured resin sdk 2017-01-25 19:33:43 +01:00
7a4de5357e Fix some issues with Resin-SDK usage after initial testing 2017-01-25 19:25:12 +01:00
5bbb055cd9 Formally depend on the new resin-sdk release 2017-01-25 19:25:12 +01:00
553b96e48f add the CS preamble to the generated files 2017-01-25 19:25:12 +01:00
7a0e8beb07 update for the new resin.models.device.generateUniqueKey 2017-01-25 19:25:12 +01:00
f17cbb1205 use the new SDK factory 2017-01-25 19:25:12 +01:00
b690060bc4 update deps and remove unused 2017-01-25 19:25:12 +01:00
72b2058d27 Merge pull request #418 from resin-io/lekkas-patch-1
docs: rename 'Git Bash' to 'Git for Windows'
2017-01-18 19:46:40 +02:00
bbd617ea76 docs: rename 'Git Bash' to 'Git for Windows' 2017-01-18 19:33:49 +02:00
f9a4f8c375 v5.2.4 2017-01-18 19:14:52 +02:00
5c289a6e00 Merge pull request #417 from resin-io/fix-cmd-exe-doc-error
Fix requirement docs for resin sync and resin ssh
2017-01-18 19:07:36 +02:00
3b439282ae Regenerate JS and add header to suppress diff 2017-01-18 18:04:42 +02:00
d473509675 Docs: fix requirements for resin ssh and resin sync
change-type: patch
2017-01-18 18:04:37 +02:00
49664b815d v5.2.3 2017-01-04 13:54:09 -04:00
06cc863f02 Merge pull request #412 from resin-io/411-fix-add-js-yaml
add missing dependency - fix #411
2016-12-16 17:38:57 +03:00
099cf997cb add missing dependency - fix #411 2016-12-16 17:21:05 +03:00
e8183d4031 v5.2.2 2016-11-01 14:38:55 -04:00
6954da4a24 Merge pull request #407 from resin-io/add-shutdown-command
Actually add the shutdown command
2016-11-01 12:42:21 -04:00
c18e8f1dbd Actually add the shutdown command 2016-11-01 12:42:29 -03:00
741b53ad7e Merge pull request #406 from resin-io/rebuild-coffeescript
Rebuild CoffeeScript
2016-10-28 16:45:56 -04:00
8282785b2a Rebuild CoffeeScript
See https://github.com/resin-io/resin-cli/pull/405#issuecomment-256998739
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
2016-10-28 16:45:18 -04:00
e013986dba v5.2.1 2016-10-28 12:17:25 -04:00
3083ff0640 Merge pull request #405 from resin-io/fix-capitano-error
Fix `Boolean options can't have parameters` error
2016-10-28 12:16:09 -04:00
01cad3c048 Fix Boolean options can't have parameters error
This error was introduced as part of
`9cf42462c029e038e09efc961736946be8bfcb9b`, since the `forceUpdateLock`
option being used in the `reboot` command contains a `parameter`
property despite being declared a boolean.

Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
2016-10-28 12:11:57 -04:00
c9919a90a8 v5.2.0 2016-10-27 17:47:43 -04:00
0bf6bcc430 Merge pull request #404 from resin-io/reboot-shutdown-force
Add a device shutdown command, and allow forcing reboot and shutdown
2016-10-27 18:42:30 -03:00
9cf42462c0 Add a device shutdown command, and allow forcing reboot and shutdown 2016-10-27 18:39:31 -03:00
0f4eca2ff0 Update resin-sdk to 5.4.0 2016-10-27 12:02:37 -03:00
d414d1c5e3 Merge pull request #403 from resin-io/coffee-script-1.11.1
Update coffee-script to 1.11.1
2016-10-26 12:04:18 -04:00
afe98ff37d Update coffee-script to 1.11.1 2016-10-26 11:55:39 -03:00
ce026ea387 v5.1.0 2016-09-25 19:55:55 -04:00
25f8f3d740 Merge pull request #397 from resin-io/regenerate-docs
Regenerate markdown documentation
2016-09-25 19:52:41 -04:00
f719f5c948 Regenerate markdown documentation
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
2016-09-25 19:51:32 -04:00
e2cb79edb1 Merge pull request #396 from resin-io/devices-supported
Bring back `devices supported` command
2016-09-25 19:51:02 -04:00
c6e669fa6b Bring back devices supported command
Fixes: https://github.com/resin-io/resin-cli/issues/394
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
2016-09-25 19:49:06 -04:00
7953f6f690 5.0.0 2016-09-15 19:45:23 +03:00
baf367b276 Merge pull request #367 from resin-io/features/resin-sync-ssh-improvements
resin sync/ssh improvements
2016-09-15 09:21:24 -07:00
7fecb53cdf resin sync/ssh: update docs 2016-09-15 13:09:25 +03:00
b4edb7ed7f resin sync/ssh: generate JS 2016-09-15 13:09:25 +03:00
16a1741374 resin sync: always display device selection dialog when uuid is not passed as an argument
Closes #375
2016-09-15 13:09:25 +03:00
e0a2217b94 resin sync/ssh: always display selection dialog, even for a single online device
Closes #373
2016-09-15 13:09:25 +03:00
7bd8a683b2 resin sync: remove quotes from --source/--destination help as it implies they're required
Closes #372
2016-09-15 13:09:25 +03:00
6b00bbc73a resin sync: add --after option 2016-09-15 13:09:25 +03:00
42d0b52df7 resin ssh: disable ControlMaster ssh option
This change was necessary because our ssh gateway does not
support ControlMaster mode.

Closes #366
2016-09-15 13:09:25 +03:00
97c768edcd resin sync: add --skip-gitignore option. Improve help section wording 2016-09-15 13:09:25 +03:00
10a0924cd7 resin sync: load uuid from .resin-sync.yml if possible 2016-09-15 13:09:25 +03:00
fdb8bf6967 resin sync: add --destination option and require --source if .resin-sync.yml is missing
Closes #359
2016-09-15 13:09:25 +03:00
81d8974213 resin-sync: update to resin-sync@3.0.0 2016-09-15 13:09:25 +03:00
af8d20ea3f resin sync/ssh: only accept uuid as destination
Also change --port option alias to '-p' from '-t'
2016-09-15 13:09:25 +03:00
7d606568f6 v4.5.0 2016-09-14 11:09:43 -07:00
de13337655 Merge pull request #392 from resin-io/manifest-first-partition
Attempt to get device type from the image first partition
2016-09-14 11:08:19 -07:00
8b485b5ad5 Attempt to get device type from the image first partition
New images will ship a `device-type.json` file in the first partition,
which we can use instead of querying the API for certain configuration
and initialisation commands.

If the file is not found, or is malformed, we still fallback to the API.

Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
2016-09-14 11:06:17 -07:00
fbccf8a465 Merge pull request #393 from resin-io/upgrade-resin-device-init
Upgrade `resin-device-init` to v2.1.0
2016-09-14 11:05:28 -07:00
ce50d8b73d Upgrade resin-device-init to v2.1.0
Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
2016-09-14 11:03:51 -07:00
2088cbe896 v4.4.0 2016-08-11 10:35:19 -04:00
870ce974e0 Merge pull request #387 from resin-io/Add_OS_VERSION
Add OS version to devices,device commands and Supervisor Version to devices command
2016-08-11 10:24:00 -04:00
5f8c261288 v4.3.0 2016-08-11 10:21:41 -04:00
cb386d15aa Add OS version to devices,device commands and Supervisor Version to devices command 2016-08-10 18:42:34 +05:30
5ed26250d2 Merge pull request #384 from resin-io/features/deviceUrl
Implement `device enableDeviceUrl/disableDeviceUrl/hasDeviceUrl/getDeviceUrl`
2016-08-09 16:45:46 -04:00
7b0415a270 Switch to more human-like command name and follow convention from dashboard 2016-08-09 22:44:40 +05:30
3adb8f19bd Implement device enableDeviceUrl/disableDeviceUrl/hasDeviceUrl/getDeviceUrl 2016-08-09 18:45:24 +05:30
d81fbad6f3 Merge pull request #376 from resin-io/feature/global-help-option
Add global --help option
2016-07-31 20:41:28 -04:00
a70e38ef12 Add global --help option 2016-07-29 15:32:12 +03:00
aeba64b1ee v4.2.1 2016-07-26 13:04:21 -04:00
e6e44474a4 Merge pull request #371 from resin-io/upgrade-sdk
Upgrade `resin-sdk` to v5.3.5
2016-07-26 13:03:18 -04:00
ea44c0571b Upgrade resin-sdk to v5.3.5
This version contains a fix for `undefined` logs. See:

- https://github.com/resin-io/resin-sdk/pull/217
- https://github.com/resin-io/resin-device-logs/pull/14

Fixes: https://github.com/resin-io/resin-cli/issues/370
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
2016-07-26 13:01:25 -04:00
f68364b695 Merge pull request #365 from resin-io/feature/sync-ssh-device-autochoose
Feature/sync ssh device autochoose
2016-07-07 22:21:37 +03:00
81a6843c93 resin ssh: Implement device inference and autoselect if there is a single one 2016-07-07 21:51:53 +03:00
b672ff1fa1 resin sync: change argument/help to a more meaningful 'resin sync [destination]' 2016-07-07 20:37:11 +03:00
b4c89e812c Merge pull request #364 from resin-io/feature/sync-filter-offline-devices
resin sync: filter out offline devices in interactive choosing dialog
2016-07-07 13:14:57 -04:00
68808e760e resin sync: filter out offline devices in interactive choosing dialog 2016-07-07 19:12:39 +03:00
69c98f4afb v4.2.0 2016-06-22 16:19:31 -04:00
509fef754e Merge pull request #356 from resin-io/feature/resin-sync-verbose
Support verbose flag for resin sync
2016-06-22 16:18:34 -04:00
6d1d4dc173 Support verbose flag for resin sync 2016-06-22 17:59:14 +03:00
0d7d6de7cd v4.1.0 2016-06-22 09:46:18 -04:00
c5452f9304 Merge pull request #354 from resin-io/feature/resin-ssh-verbose
Support --verbose/-v flag in resin ssh
2016-06-22 09:37:03 -04:00
12854db923 Support --verbose/-v flag in resin ssh
Closes https://github.com/resin-io/resin-cli/issues/353
2016-06-22 14:57:43 +03:00
51f9e18f6a Merge pull request #352 from resin-io/feature/reword-config-help
Reword config help instructions
2016-06-14 12:10:05 -04:00
29c20e32f6 Reword config help instructions
Closes #351
2016-06-14 19:08:25 +03:00
995194fe2c Merge pull request #346 from resin-io/misc/ssh-sync-cmd-exe-warning
Clarify that sync and ssh only support cmd.exe on Windows
2016-05-19 10:17:28 -04:00
7c784909fd Merge pull request #345 from resin-io/change-resin-ssh-loglevel
Change resin ssh loglevel from QUIET to ERROR
2016-05-19 10:17:20 -04:00
a90d568d5c Clarify that sync and ssh only support cmd.exe on Windows
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
2016-05-19 10:10:45 -04:00
a265063fa1 Change resin ssh loglevel from QUIET to ERROR 2016-05-19 16:52:23 +03:00
1c5945d3ae v4.0.3 2016-05-17 10:00:04 -04:00
6062c7a306 Merge pull request #344 from resin-io/feat/ssh-windows-support
Make resin ssh work in Windows cmd.exe
2016-05-09 12:06:31 -04:00
b061644b19 Make resin ssh work in Windows cmd.exe
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
2016-05-06 13:12:55 -04:00
38b97baf02 Add v4.0.2 entry to CHANGELOG
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
2016-04-27 11:19:35 -04:00
8315bcb7db Improve CHANGELOG v4.0.0 entry prose
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
2016-04-27 11:19:21 -04:00
e8fa76266f 4.0.2 2016-04-27 01:33:09 +03:00
3db3baeb7c Merge pull request #343 from resin-io/update-resin-sync-version
Update resin sync version to 2.0.2
2016-04-27 01:31:41 +03:00
17550f9bc9 Update resin sync version to 2.0.2 2016-04-27 01:29:45 +03:00
44f80f7a39 v4.0.1 2016-04-26 12:40:20 -04:00
372fa01cf3 Merge pull request #342 from resin-io/fix/unhandled-ssh-exceptions
Return control to capitano at the end of the ssh command
2016-04-26 12:39:01 -04:00
9a515ef4e3 Return control to capitano at the end of the ssh command
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
2016-04-26 12:37:39 -04:00
6beb6ff44e 4.0.0 2016-04-26 16:17:01 +03:00
c2cd10f6a7 Update changelog 2016-04-26 16:16:02 +03:00
4d3769def8 Merge pull request #337 from resin-io/enter-container-command
Implement resin enter <uuid> command
2016-04-26 16:13:00 +03:00
42bfb3b0cc Implement resin ssh <uuid> command 2016-04-26 16:00:54 +03:00
d9c2717fe4 Merge pull request #340 from resin-io/upgrade-sync-packages
Use resin-sync v2.0.1 and resin-sdk v5.3.0
2016-04-26 15:57:23 +03:00
8e93577f90 Use resin-sync v2.0.1 and resin-sdk v5.3.0 2016-04-26 15:55:21 +03:00
5b7e1b656e Merge pull request #339 from resin-io/feat/infer-or-select-device
Implement a function to attempt to infer a device selection
2016-04-25 14:30:36 -04:00
4a05ce3f53 Attempt to infer the device uuid in resin sync
This PR adds functionality to `resin sync` to try to infer what the
device uuid is as follows:

- If the argument to `resin sync` is an app, get all the devices from
that application. If there is only one, auto-select it, otherwise show
an interactive drive selection widget.

- If the argument to `resin sync` is a uuid, use it directly, without
trying to infer anything.

- If no argument is passed to `resin sync`, display an interactive
selection widget showing all your devices from all your applications.

Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
2016-04-25 08:57:19 -04:00
61574a8522 Merge pull request #338 from resin-io/change-sync-description
Change sync description
2016-04-25 08:37:33 -04:00
9400d4027a Update resin-settings-client version 2016-04-25 14:47:15 +03:00
b5ec49dda1 Remove resin sync 'exec after rsync' feature as it's not supported by
ssh gateway
2016-04-25 14:47:05 +03:00
1c66efb4fa v3.0.2 2016-04-08 13:30:40 -04:00
68fca3d030 Merge pull request #336 from resin-io/fix/os-configure-shorter-uuids
Fix shorter uuids not working with the `os configure` command
2016-04-08 13:29:35 -04:00
325304aebe Fix shorter uuids not working with the os configure command
`resin-device-init`, which is used by the `os configure` command was
still running an older SDK version, that didn't support shorter uuids.

Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
2016-04-08 13:28:21 -04:00
a50cf5b198 Merge pull request #335 from resin-io/doc/plugin-development
Link to resin-plugin-hello from README
2016-03-31 10:19:48 -04:00
1b7aeeafc1 Link to resin-plugin-hello from README 2016-03-31 10:11:05 -04:00
6e3c2ef168 v3.0.1 2016-03-29 11:30:11 -04:00
d9b4753690 Merge pull request #334 from resin-io/fix/event-explosion
Log CLI events based on original command signature
2016-03-29 11:29:17 -04:00
ca40d7ca65 Log CLI events based on original command signature
Currently we log a CLI event with the passed command, however this might
include usr params, like a uuid, and therefore cause thousands of
different event names in Mixpanel.
2016-03-29 11:27:53 -04:00
4aa8362be9 v3.0.0 2016-03-28 09:29:31 -04:00
d68b61a913 Merge pull request #333 from resin-io/misc/regenerate-docs
Regenerate docs and include sync
2016-03-28 09:26:08 -04:00
20969ef249 Regenerate docs and include sync 2016-03-28 09:25:40 -04:00
469d35fcc1 Merge pull request #332 from resin-io/feat/sync
Integrate sync plugin in the Resin CLI
2016-03-28 09:22:36 -04:00
e9b8c38eeb Integrate sync plugin in the Resin CLI 2016-03-28 09:21:25 -04:00
d4c44bf350 Merge pull request #331 from resin-io/feat/config-generate-application
Allow generating a config.json from an application with config generate
2016-03-21 15:46:52 -04:00
2d8cf7c479 Allow generating a config.json from an application with config generate
Currently, `config generate` requires a device uuid. The command now
accepts either a uuid or an application name, and generates a
config.json accordingly.
2016-03-21 15:42:54 -04:00
ca6e715bfa Merge pull request #330 from resin-io/upgrade/device-config
Upgrade resin-device-config to v3.0.0
2016-03-21 15:05:28 -04:00
3a839c947e Upgrade resin-device-config to v3.0.0 2016-03-21 15:01:59 -04:00
9896de3c34 Merge pull request #329 from resin-io/feat/config-inject
Implement config inject command
2016-03-18 09:13:19 -04:00
03d7520de2 Implement config inject command
This command allows to user to inject a whole `config.json` file to a
provisioned device.
2016-03-17 16:07:19 -04:00
4fc8b130f8 Merge pull request #328 from resin-io/fix/innacurate-invalid-2fa-code
Only throw "Invalid 2FA code" if we're sure that's the cause
2016-03-17 15:38:15 -04:00
c5692f8b13 Only throw "Invalid 2FA code" if we're sure that's the cause
Currently, such error will be thrown when
`resin.auth.twoFactor.challenge()` rejects, but an invalid code is not
the only thing this function can reject for.
2016-03-17 15:37:02 -04:00
85561b5d50 Merge pull request #326 from resin-io/doc/eaccess-login
Document the case where EACCES is thrown during login
2016-03-09 11:04:17 -04:00
88c0833ae2 Document the case where EACCES is thrown during login 2016-03-09 11:03:45 -04:00
7df78a0c9e Merge pull request #325 from resin-io/misc/update
Force update alert to be always shown
2016-03-08 09:26:22 -04:00
30663b0301 Force update alert to be always shown
If `updateCheckInterval` has any meanginful value, the alert will be
shown one out of ten times, or something like that, making the user
likely to miss updates.

The underlying issue is that `update-notifier`, if it detects a cached
update notification, it deletes it, and only attempts to show it back if
`updateCheckInterval` is greater than `Date.now() - lastUpdateCheck`.
2016-03-08 09:23:03 -04:00
4ffba0ed56 Merge pull request #324 from resin-io/upgrade/dependencies
Upgrade most outdated dependencies
2016-03-07 11:42:10 -04:00
a522c70f92 Upgrade most outdated dependencies 2016-03-07 08:44:10 -04:00
f295837840 v2.7.0 2016-03-07 08:34:32 -04:00
091ae8eb03 Merge pull request #322 from resin-io/feat/device-reboot
Implement device reboot command
2016-03-07 08:33:14 -04:00
6405c6bb6f Implement device reboot command
Fixes: https://github.com/resin-io/resin-cli/issues/319
2016-03-04 09:38:11 -04:00
797122ce37 Merge pull request #320 from resin-io/feat/config-generate
Implement config generate command
2016-02-29 08:40:15 -04:00
f81db5a775 Merge pull request #321 from resin-io/misc/regenerate-docs
Regenerate docs
2016-02-26 22:40:53 -04:00
0f84aea47d Regenerate docs 2016-02-26 22:38:16 -04:00
84ed20d3ec Implement config generate command
This command allows the user to generate a config.json file and either
print it to stdout or save it to a file.
2016-02-26 22:37:15 -04:00
9870727e36 v2.6.2 2016-02-19 08:36:25 -04:00
db3de2137b Remove hardcoded readdir path
This was probably used for debugging at some point and got commited
accidentally.
2016-02-19 08:35:10 -04:00
92c6af91ca Merge pull request #317 from imrehg/badge
badge fixes
2016-02-13 22:18:18 -04:00
d578f62dca escape gitter image link
This is to fix bage link on npmjs.org, hopefully, as currently
it seems it cannot handle real spaces in image URLs.
2016-02-13 09:27:42 +08:00
d4527220c3 fix dependencies badge link 2016-02-13 09:21:08 +08:00
d5797124f5 Merge pull request #316 from resin-io/misc/mbr-error-troubleshotting
Move Invalid MBR error explanation to TROUBLESHOOTING
2016-02-12 19:26:14 -04:00
654c3c627d Move Invalid MBR error explanation to TROUBLESHOOTING 2016-02-12 14:45:00 -04:00
3953b00e77 v2.6.1 2016-02-12 14:41:37 -04:00
13f33da280 Merge pull request #315 from resin-io/feat/capitano-analytics
Inject analytics in Capitano
2016-02-12 14:38:31 -04:00
356d2ef6b2 Inject analytics in Capitano 2016-02-12 14:34:16 -04:00
36a8179e0c Merge pull request #314 from resin-io/upgrade/form
Upgrade resin-cli-form to v1.4.0
2016-02-11 12:54:33 -04:00
aedb9c732f Upgrade resin-cli-form to v1.4.0 2016-02-11 12:49:56 -04:00
c3bd433532 Merge pull request #313 from resin-io/feat/devices-is-online
Show if a device is online in devices command
2016-02-11 09:50:43 -04:00
d72750de65 Show if a device is online in devices command
Fixes: https://github.com/resin-io/resin-cli/issues/312
2016-02-10 08:51:00 -04:00
ccd8e73c4e Merge pull request #310 from resin-io/fix/309/device-name-help
Refer to device uuids in command help
2016-01-28 09:11:25 -04:00
6c677fe8cd Refer to device uuids in command help
Currently the CLI asks for a device "name" on device options while it
actually needs a "uuid".

Fixes: https://github.com/resin-io/resin-cli/issues/309
2016-01-28 09:10:11 -04:00
eaa1a798c5 Merge pull request #308 from resin-io/upgrade/image-manager
Upgrade Resin Image Manager to v4.0.0
2016-01-26 16:07:30 -04:00
da1b446b3b Upgrade Resin Image Manager to v4.0.0 2016-01-26 15:38:58 -04:00
18b4509fef Merge pull request #307 from resin-io/feature/device-status
Show parsed device status in device command
2016-01-26 15:09:03 -04:00
35bba04b16 Show parsed device status in device command 2016-01-26 12:16:55 -04:00
dd382158dd Merge pull request #306 from resin-io/misc/messages
Improve messages
2016-01-25 09:50:56 -04:00
40f015de93 Fix grammar issue: Administration -> Administrative 2016-01-25 09:00:04 -04:00
bee523828a Improve awaitDevice success message 2016-01-25 08:59:09 -04:00
474635401a Merge pull request #305 from resin-io/doc/mbr-error
Document corruped image MBR error
2016-01-24 18:16:35 -04:00
a8c30bb395 Document corruped image MBR error 2016-01-22 10:14:55 -04:00
9e00fdaf31 Merge pull request #304 from resin-io/doc/readme-header
Improve README header style
2016-01-21 23:18:36 -04:00
5f83c870ed Improve README header style
- Use SVG badges.
- Set description as a blockquote before the title.
2016-01-21 23:17:02 -04:00
5d89533afc Merge pull request #303 from resin-io/doc/login-error
Add information on how to login on user permission error
2016-01-21 23:12:20 -04:00
a346c3f043 Add information on how to login on user permission error 2016-01-21 23:07:08 -04:00
de0649c980 v2.6.0 2016-01-21 15:59:52 -04:00
b7300deab7 Merge pull request #302 from resin-io/upgrade/manager
Upgrade Resin Image Manager to v3.2.6
2016-01-21 15:50:45 -04:00
70b2ba3ab9 Upgrade Resin Image Manager to v3.2.6 2016-01-21 15:49:44 -04:00
8cbf792786 Merge pull request #301 from resin-io/doc/remove-ci-badges
Remove build status CI badges from README
2016-01-21 10:43:46 -04:00
bca9a7f51f Remove build status CI badges from README
CI integration was removed in
96f0b5fbd6,
but it looks like we forgot to remove the badges.
2016-01-21 10:42:45 -04:00
4bf079377b Merge pull request #300 from resin-io/feat/shorter-uuids
Support shorter uuids
2016-01-21 10:41:55 -04:00
ebefd816b6 Show shorter uuids when listing devices 2016-01-21 10:26:13 -04:00
fb1ef0df63 Promote shorter uuids in all examples 2016-01-21 10:23:40 -04:00
4489b1daa0 Merge pull request #299 from resin-io/misc/remove-tests
Remove tests tasks and CI configuration
2016-01-21 10:18:53 -04:00
96f0b5fbd6 Remove tests tasks and CI configuration
We don't have any tests in this repository
2016-01-21 10:16:54 -04:00
f6897ad41f Merge pull request #298 from resin-io/upgrade/sdk
Upgrade Resin SDK to v5.0.1
2016-01-21 10:14:56 -04:00
69e031da28 Upgrade Resin SDK to v5.0.1
This breaking change doesn't affect the CLI in any way, so we can
upgrade directly.
2016-01-21 10:13:15 -04:00
c61a7ef94a Merge pull request #296 from resin-io/ci/notifications
Improve Travis and Appveyor notifications
2016-01-18 11:32:48 -04:00
65bc22c02c Merge pull request #295 from resin-io/fix/delete-device-resource-on-errors
Remove registered device resource in case of errors in quickstart
2016-01-14 10:58:18 -04:00
1d55ea4dcf Improve Travis and Appveyor notifications 2016-01-14 10:49:11 -04:00
add30b33a1 Remove registered device resource in case of errors in quickstart
A device resource needs to be registered with the API before being able
to create the `config.json` file that goes in a device.

This means thats the device image is configured and written to an
external drive (e.g: SDCard) *after* the device resource registered.

If any of the above operations fail, there will be an unitialized orphan
device living in the selected application that the user will have to
remove himself.
2016-01-14 09:14:45 -04:00
999120e711 Merge pull request #293 from resin-io/doc/faq
Improve FAQ
2016-01-13 09:34:22 -04:00
67ce0f7f2d Merge pull request #294 from resin-io/feat/login-register
Redirect users to signup from login if they don't have an account
2016-01-13 09:34:16 -04:00
4645ad06bc Redirect users to signup from login if they don't have an account 2016-01-13 00:25:31 -04:00
612437ae58 Improvee FAQ with more frequent questions 2016-01-13 00:20:18 -04:00
35a821b904 Improve FAQ staging resin url title 2016-01-13 00:13:07 -04:00
e4359834d6 Rename README documentation section to FAQ 2016-01-13 00:12:04 -04:00
ef7e39450c Merge pull request #292 from resin-io/feat/ux-improvements
UX CLI improvements
2016-01-12 14:28:47 -04:00
c3a5998d5c Reuse messages 2016-01-12 10:45:32 -04:00
78ab2af8ba Print verbose help in resin help command 2016-01-12 10:39:29 -04:00
11354de596 Print an informative message after successful login 2016-01-12 10:30:56 -04:00
86cac606e4 Add Resin.io ASCII art in login 2016-01-12 10:23:46 -04:00
9b052c9aa5 Handle authentication in quickstart
If the user is not logged in, make quickstart prompt for authentication
automatically.
2016-01-12 10:12:44 -04:00
8d709aea7d Implement purely interactive login command
The new login command interactively asks the user if he wants to login
using web/credentials/token.
2016-01-12 09:08:03 -04:00
70ea8dd1a3 Redirect users to GitHub and Gitter in case of errors
Users will ge a better experience by knowing exactly where to go for
help if things go wrong.
2016-01-12 08:31:40 -04:00
11c0d2a847 Merge pull request #289 from resin-io/help/device-init-primary
Don't make `device init` a primary command
2016-01-12 08:08:38 -04:00
301b8a6ba3 Merge pull request #290 from resin-io/doc/quickstart-sudo
Stop instructing users to run quickstart as root
2016-01-12 08:08:34 -04:00
acb0aa445c Merge pull request #291 from resin-io/doc/support-gitter
Point users to Gitter in Support README section
2016-01-12 08:08:29 -04:00
a90d578c85 Point users to Gitter in Support README section 2016-01-11 16:04:22 -04:00
d859228aa9 Stop instructing users to run quickstart as root
Elevation is asked in specific steps automatically.
2016-01-11 16:01:40 -04:00
2be105d329 Don't make device init a primary command
Unlikely that a user will run this directly having the more high level
`quickstart`.
2016-01-11 16:00:07 -04:00
6d48fcfd6f Merge pull request #288 from resin-io/misc/build
Build CoffeeScript files with LICENSE additions
2016-01-11 15:59:06 -04:00
551a315432 Build CoffeeScript files with LICENSE additions 2016-01-11 15:58:35 -04:00
351dfdb892 Merge pull request #287 from resin-io/feat/gitter-badge
Add Gitter badge
2016-01-11 12:19:06 -04:00
dc6727fbf1 Add Gitter badge 2016-01-11 11:28:33 -04:00
af88e48c39 Merge pull request #285 from resin-io/doc/license
Change license to Apache 2.0
2016-01-04 10:25:33 -04:00
9cfce68489 Change license to Apache 2.0 2016-01-03 23:58:51 -04:00
8393ff647c Merge pull request #283 from resin-io/doc/cli-staging
Document how to point the CLI to staging
2016-01-01 02:19:43 -04:00
63122a5f51 Merge pull request #282 from resin-io/feat/credential-auth
Add optional credential-based authentication
2015-12-31 22:02:31 -04:00
0ad4598575 Document how to point the CLI to staging 2015-12-31 22:00:39 -04:00
b71c28cec0 Add optional credential-based authentication 2015-12-12 00:11:04 -04:00
b0ab23dad4 v2.5.0 2015-12-11 21:30:02 -04:00
de9297c351 Merge pull request #281 from resin-io/feat/logs-timestamp
Add timestamp to logs lines
2015-12-11 21:26:21 -04:00
26e3cb7957 Add timestamp to logs lines 2015-12-11 20:28:29 -04:00
bb78a3ca09 Merge pull request #280 from resin-io/feat/lazy-loading
Lazy load command actions dependencies
2015-12-07 16:21:32 -03:00
210680c9c9 Lazy load command actions dependencies
In my system (MBPr 13), printing the current version takes over 2
seconds:

```sh
$ time ./bin/resin version
2.4.0
./bin/resin version  1.37s user 0.19s system 73% cpu 2.130 total
```

The CLI takes almost all of these time to parse the dependency tree
before returning control over the actually called command.

To mitigate this problem, we only require the NPM dependencies a command
requires when executing such command, and thus prevent dependencies from
being required and parsed unnecessary.

After this improvement, printing the original example (`resin version`)
returns in less than a second (2x improvement):

```sh
$ time ./bin/resin version
2.4.0
./bin/resin version  0.88s user 0.09s system 102% cpu 0.938 total
```
2015-12-07 11:48:54 -03:00
6810eb31fd Merge pull request #279 from resin-io/jviotti/feat/npmignore
Add npmignore
2015-12-04 11:02:01 -04:00
f5b6df483d Merge pull request #278 from resin-io/jviotti/doc/readme-inst
Add --production to installation instructions
2015-12-04 10:55:56 -04:00
7d0da7adc0 Merge pull request #277 from resin-io/jviotti/feat/token-login
Support for login in with token
2015-12-04 10:55:39 -04:00
785f2b4ef5 Add npmignore 2015-12-04 08:51:28 -04:00
b668a8c7d0 Add --production to installation instructions 2015-12-04 08:31:39 -04:00
2e247faae4 Merge pull request #276 from resin-io/jviotti/feat/quickstart-after-instructions
Add helpful instructions after quickstart
2015-12-03 10:24:13 -04:00
3997a61b78 Support for login in with token
This is useful in the scenario when the user is using the CLI in an
environment in which he/she doesn't have access to a web browser, like a
headless server or a Vagrant development environment.
2015-12-03 10:22:22 -04:00
8ef27f0525 Add helpful instructions after quickstart 2015-12-02 16:28:32 -04:00
20855be968 Merge pull request #275 from resin-io/jviotti/feat/sudo-explain
Explain why we need the computer password on device init
2015-12-02 15:59:27 -04:00
800d13e3cd Merge pull request #274 from resin-io/jviotti/feat/id-devices
Show id in devices command
2015-12-02 15:59:21 -04:00
abc8399260 Explain why we need the computer password on device init 2015-12-02 11:15:20 -04:00
9ad2ba1131 Show id in devices command 2015-12-02 09:06:41 -04:00
0f8d6a98e3 v2.4.0 2015-12-01 14:43:53 -04:00
81af8c74b4 Merge pull request #273 from resin-io/jviotti/update/doc
Update generated documentation
2015-12-01 14:41:29 -04:00
16ab74294f Update generated documentation 2015-12-01 12:56:53 -04:00
f8bcc9d1ea Merge pull request #272 from resin-io/jviotti/feature/automatic-login-exchange
Perform automatic token exchange with the dashboard
2015-12-01 12:56:07 -04:00
9a89e3c3ca Perform automatic token exchange with the dashboard 2015-11-25 09:25:08 -04:00
359c37f259 Merge pull request #270 from resin-io/jviotti/fix/remove-debug-download-msg
Simplify download output message
2015-11-24 11:07:40 -04:00
1ba5697986 Merge pull request #271 from resin-io/jviotti/dependencies/remove-unused
Remove unused dependencies
2015-11-24 11:07:21 -04:00
647ed1e7aa Remove unused dependencies 2015-11-24 00:06:44 -04:00
b881e23c1c Merge pull request #269 from resin-io/jviotti/feature/device-type-in-app-list
Show device types when selecting applications
2015-11-23 23:39:34 -04:00
ae8621dc81 Simplify download output message
The message displayed the output of the download, which was mainly used
for debugging purposes when developing `device init` and `quickstart`.
2015-11-23 23:38:28 -04:00
e08c3752f9 Show device types when selecting applications
Some CLI commans prompt to select an existing application, presending a
dropdown with all the application names, however it's hard to remember
which application belon to which device type, which makes it easier to
select the wrong application.
2015-11-23 09:23:08 -04:00
65646d1206 v2.3.0 2015-11-20 13:01:58 -04:00
e42d3e8c4c Merge pull request #266 from resin-io/jviotti/feature/resin-url-info
Clarify resin url on auth and whoami
2015-11-18 16:15:46 -04:00
a4642f6184 Clarify resin url on auth and whoami
When you change the `resinUrl` config from time to time it can be
confusing to remember where you're logging in, or in which host you're
in.

Currently I have to check the configuration files/environment variables
manually or run `resin settings`.

This PR prints the detected resin url on `resin login` and `resin
whoami` so it's always clear where you are.
2015-11-16 10:11:08 -04:00
038c871911 Merge pull request #265 from resin-io/jviotti/feature/settings
Implement settings command
2015-11-16 09:03:20 -04:00
f52dd2976f Implement settings command
This command allows the user to list all detected settings.
2015-11-15 22:08:02 -04:00
d079a57da4 Merge pull request #264 from resin-io/jviotti/fix/devices-uuid
Show uuid in devices command
2015-11-13 15:33:17 -04:00
43697a3476 Show uuid in devices command
The command to get information about a device, `resin device` requires a
`uuid` as a parameter. Given that we don't show uuids in `resin
devices`, the user has no way to know what uuid to pass to get extra
information.

We also remove some non very used information columns from `resin
devices` to make space for the uuid.
2015-11-13 14:06:55 -04:00
b893bd1e39 Merge pull request #262 from resin-io/jviotti/feature/windows-elevation
Handle Windows permissions elevation automatically
2015-11-12 14:05:44 -04:00
c3b5a768e1 Handle Windows permissions elevation automatically 2015-11-12 13:30:48 -04:00
111ea44b40 Resin CLI v2.2.0 2015-11-12 08:33:51 -04:00
d1b25c17b6 Merge pull request #261 from resin-io/jviotti/feature/move-device
Implement device move command
2015-11-12 08:32:55 -04:00
ba318f2939 Implement device move command
This command allows to user to move a device to another application he
owns.
2015-11-12 07:47:16 -04:00
7ac0291c53 Merge pull request #260 from resin-io/jviotti/102/feature/device-reconfigure
Implement config reconfigure command
2015-11-11 10:59:31 -04:00
f64676ab98 Implement config reconfigure command
This command allows the user to reconfigure an already provisioned
device.

Fixes: https://github.com/resin-io/resin-cli/issues/102
2015-11-11 10:38:45 -04:00
d522cbe1ca Merge pull request #259 from resin-io/jviotti/refactor/resin-config-json
Make use of resin-config-json for config commands
2015-11-11 10:27:24 -04:00
4fc7a4e436 Make use of resin-config-json for config commands
This module encapsulates the low level details of `config.json` I/O and
tests them extensively.

See: https://github.com/resin-io/resin-config-json
2015-11-11 10:04:46 -04:00
79f2b4f0d5 Merge pull request #258 from resin-io/jviotti/doc/regenerate
Regenerate documentation
2015-11-11 09:17:52 -04:00
880a7b1e25 Regenerate documentation 2015-11-11 08:45:38 -04:00
89c5bb3080 Resin CLI v2.1.0 2015-11-11 08:31:32 -04:00
a71fb8ca4d Merge pull request #257 from resin-io/jviotti/feature/config-json-write
Implement config write command
2015-11-11 08:29:10 -04:00
3b35aed3bf Implement config write command
This command allows the user to overwrite properties of the config.json
file.
2015-11-10 14:28:18 -04:00
0bc3b9460f Merge pull request #256 from resin-io/jviotti/feature/read-config
Implement config read command
2015-11-10 13:08:54 -04:00
5509a3e9fd Implement config read command
This command is used to read a config.json from a provisioned device
2015-11-10 12:53:34 -04:00
f84d0d0980 Merge pull request #253 from resin-io/jviotti/refactor/sudo-president
Use president to provide sudo functionality
2015-10-27 07:58:17 -04:00
c866f6e46c Use president to provide sudo functionality 2015-10-26 09:47:49 -04:00
3192f9d2ef Merge pull request #252 from resin-io/jviotti/fix/239/sudo-explain
Clarify the need of computer password during sudo
2015-10-26 09:42:26 -04:00
de83a06db8 Clarify the need of computer password during sudo
Since we only prompt "Password:", it might be confusing for some users
that think they have to enter their Resin.io password instead.

Fixes: https://github.com/resin-io/resin-cli/issues/239
2015-10-26 08:49:41 -04:00
9260c8dce2 Resin CLI v2.0.1 2015-10-26 08:46:16 -04:00
bea5f22732 Merge pull request #251 from resin-io/jviotti/fix/stream-wait-sudo
Refer to the correct waiting stream function
2015-10-26 08:45:14 -04:00
363f12f81b Refer to the correct waiting stream function
We recently changed to using `rindle`, however looks like we forgot
to replace this particular instance.
2015-10-26 08:34:07 -04:00
1cbd33679f Resin CLI v2.0.0 2015-10-26 08:08:20 -04:00
e962371b59 Merge pull request #248 from resin-io/jviotti/remove/associate
Remove app associate command
2015-10-21 14:13:28 -04:00
77a3d4d6f6 Merge pull request #249 from resin-io/jviotti/fix/validation-module
Fix validation module require typo
2015-10-21 14:13:21 -04:00
fc5fe6cf68 Fix validation module require typo 2015-10-21 13:28:51 -04:00
f921488e8c Remove app associate command 2015-10-21 13:25:22 -04:00
8fe08642f5 Merge pull request #245 from resin-io/jviotti/feature/simplify-quickstart
Remove project directory creation in quickstart
2015-10-21 13:20:35 -04:00
822632718f Merge pull request #247 from resin-io/jvioti/refactor/os
Remove unused getOperatingSystem function
2015-10-21 11:43:19 -04:00
f66cd00646 Remove project directory creation in quickstart
The last part of `quickstart` feels weird. By consensus, we remove the
part that attempts to create a project directory and leave that step to
the user.
2015-10-21 11:18:29 -04:00
5498e45a35 Merge pull request #246 from resin-io/jviotti/refactor/rindle
Use rindle instead of custom waitStream
2015-10-21 11:15:12 -04:00
38479b3191 Merge pull request #244 from resin-io/jviotti/refactor/unused-npm
Remove unused npm dependency
2015-10-21 10:22:09 -04:00
965fd8fc19 Remove unused getOperatingSystem function 2015-10-21 10:20:38 -04:00
7a4f551a47 Use rindle instead of custom waitStream 2015-10-21 10:17:10 -04:00
303fd74012 Merge pull request #243 from resin-io/jviotti/feature/validation
Refactor validation to a single place
2015-10-21 10:11:46 -04:00
6a7d1b0c70 Remove unused npm dependency 2015-10-21 10:02:08 -04:00
c1e6a28640 Refactor validation to a single place 2015-10-21 09:37:25 -04:00
ec72f93480 Merge pull request #242 from resin-io/jviotti/feature/us-pw-login
Implement user/password login with 2FA support
2015-10-21 09:32:17 -04:00
8913fb515b Implement user/password login with 2FA support 2015-10-21 08:28:20 -04:00
6f91ff898f Merge pull request #241 from resin-io/jviotti/doc/drive
Regenerate os initialize documentation
2015-10-20 10:22:40 -04:00
aa949103f6 Merge pull request #240 from resin-io/jviotti/feature/device-init-advanced
Allow advanced option in device init
2015-10-20 09:47:04 -04:00
14bd4ad74e Regenerate os initialize documentation 2015-10-20 09:17:48 -04:00
f2507daa09 Allow advanced option in device init
This option is inherited to `os configure`.
2015-10-20 09:16:56 -04:00
ff81c1e514 Merge pull request #234 from resin-io/jviotti/feature/advanced-options
Ignore advanced configuration questions by default
2015-10-20 09:12:38 -04:00
7ce92b47a0 Merge pull request #238 from resin-io/jviotti/cleanup/dependencies
Remove unused resin-device-config
2015-10-19 16:04:50 -04:00
bde5cc65da Merge pull request #235 from resin-io/jviotti/fix/device-register-help
Load device info after all other device commands
2015-10-19 15:51:15 -04:00
ec9347c3a4 Merge pull request #237 from resin-io/jviotti/fix/hound
Change java_script to javascript in hound config
2015-10-19 15:51:07 -04:00
ceb8dada1d Remove unused resin-device-config 2015-10-19 15:26:09 -04:00
f4186acf80 Merge pull request #236 from resin-io/jviotti/feature/device-register-uuid
Allow passing a custom uuid to device register
2015-10-19 15:17:25 -04:00
05ce54eac1 Change java_script to javascript in hound config
They seemed to change this recently.
2015-10-19 14:21:10 -04:00
d28ecf3230 Allow passing a custom uuid to device register 2015-10-19 14:16:47 -04:00
8562f723c5 Load device info after all other device commands
This command obscures help pages for all device commands registered
afterwards since it's a common prefix for all of them.
2015-10-19 14:14:04 -04:00
f6d2043747 Merge pull request #232 from resin-io/jviotti/fix/app-create-validation
Require application name to have at least 4 characters
2015-10-19 14:06:43 -04:00
485818f3b5 Merge pull request #233 from resin-io/jviotti/fix/app-create-type
Fix --type option taking no effect in app create
2015-10-19 14:03:59 -04:00
ec28bd9c9e Ignore advanced configuration questions by default
The advanced questions can be enabled by passing `--advanced` in `os
configure`.
2015-10-19 14:02:57 -04:00
ad68dcf692 Fix --type option taking no effect in app create 2015-10-19 13:07:23 -04:00
0b7e2a2c8c Require application name to have at least 4 characters
We get a weird error message from pine otherwise:

	ResinRequestError: Request error: It is necessary that each app name
	that is of a user (Auth), has a Length (Type) that is greater than or
	equal to 4.
2015-10-19 10:56:02 -04:00
b6ebd0631a Merge pull request #229 from resin-io/jviotti/feature/os-initialize-type-option
Take device type as an option in os initialize
2015-10-15 09:59:44 -04:00
7ace4bdfa6 Merge pull request #230 from resin-io/jviotti/fix/update
Improve the way the update notifier is shown
2015-10-15 09:43:20 -04:00
1cfbd4197d Improve the way the update notifier is shown
Current has the following problems:

- Our custom message gets printed even if the notifier doesn't contain
an update.

- The notifier box is deferred, therefore it's printed at the end of the
command. Since our custom message is printed at the beginning, it makes
no sense at all.
2015-10-15 09:18:45 -04:00
b2425d2c0e Take device type as an option in os initialize 2015-10-15 09:11:38 -04:00
0ba914236a Merge pull request #228 from resin-io/jviotti/feature/os-initialize-drive
Support drive option in os initialize
2015-10-15 08:49:26 -04:00
71ee0a6cf7 Support drive option in os initialize
This allows the user to bypass the drive selection dialog.

This option can be used along with `--yes` to make the command
completely non-interactive. For example:

	$ resin os initialize rpi.img 'raspberry-pi' --drive /dev/disk2 --yes
2015-10-15 08:14:35 -04:00
4326ad4d9c Merge pull request #227 from resin-io/jviotti/fix/os-initialise-yes
Add missing `yes` option to `os initialize`
2015-10-15 07:44:33 -04:00
055bac6ff4 Merge pull request #226 from resin-io/jviotti/feature/223/update-sudo
Clarify the need for admin privileges on update
2015-10-15 07:40:15 -04:00
58713dc291 Add missing yes option to os initialize
This option is tried to be used within the command, but is not defined
as a formal Capitano option.
2015-10-14 17:49:27 -04:00
adf4aef517 Clarify the need for admin privileges on update
Fixes: https://github.com/resin-io/resin-cli/issues/223
2015-10-14 13:45:08 -04:00
a17bb2cc09 Merge pull request #220 from resin-io/jviotti/changelog/spacing
Remove spaces between lists in CHANGELOG
2015-10-13 13:04:08 -04:00
48d620f7cd Remove spaces between lists in CHANGELOG
GitHub displays the lists in a weird way otherwise.
2015-10-13 12:39:43 -04:00
fa7b104762 Resin CLI v1.1.0 2015-10-13 12:37:50 -04:00
faf2fa167f Merge pull request #219 from resin-io/jviotti/doc/troubleshoot
Remove outdated troubleshooting information
2015-10-13 12:17:46 -04:00
ad1e68427c Remove outdated troubleshooting information 2015-10-13 11:46:21 -04:00
e9147f0f6f Merge pull request #218 from resin-io/jviotti/fix/windows-edison
Avoid _.ary in temporal path disposer
2015-10-12 18:28:30 -04:00
cddf630907 Avoid _.ary in temporal path disposer
For some reason fails with a weird Bluebird error on Windows
2015-10-12 18:12:58 -04:00
494a286cae Merge pull request #217 from resin-io/jviotti/upgrade/sdk
Upgrade SDK to v3.0.0
2015-10-12 09:03:59 -04:00
e5e871ddcd Upgrade SDK to v3.0.0
Breaking changes in this version:

- `resin.models.device.generateUUID()` is now async.
2015-10-12 08:34:22 -04:00
5a3166e3de Merge pull request #215 from resin-io/jviotti/fix/await-wrap
Shorten the length of await device message
2015-10-07 12:41:57 -04:00
3149464c7a Shorten the length of await device message
If the spinner message doesn't fit in your terminal, each spinner
position will be printed in different lines.

We mitigate this by dramatically shortenning the message.
2015-10-07 11:38:59 -04:00
97d9b7816f Merge pull request #214 from resin-io/jviotti/feature/plugin-warn-red
Print plugin warnings in red as other errors
2015-10-06 19:18:33 -04:00
ec77437080 Print plugin warnings in red as other errors
For the sake of consistency.
2015-10-06 18:51:17 -04:00
d65882639a Merge pull request #211 from resin-io/jviotti/feature/101/help-topics
Separate general help per topic relevance
2015-10-02 09:12:58 -04:00
f8470287c1 Separate general help per topic relevance
Only list primary commands by default, unless a `--verbose` option is
passed to list the additional ones.

Fixes: https://github.com/resin-io/resin-cli/issues/101
2015-10-02 08:50:32 -04:00
3cc41ed62a Merge pull request #212 from resin-io/jviotti/fix/109/root-owner
Call os initialize as an elevated process
2015-10-01 14:08:19 -04:00
445e37ccaf Call os initialize as an elevated process
Currently, the fact that `os initialize` requires elevated permissions
forced us to require calling commands that reuse it, such as `device
init` and `quickstart` with administrator permissions as well.

This ended up causing issues like saving images in the cache that belong
to root, or initializing git repositories that requires `sudo` to
commit.

The solution is to call `os initialize` as a child process preppending
`sudo` within `device init`.

Fixes: https://github.com/resin-io/resin-cli/issues/109
2015-10-01 13:07:53 -04:00
ed6427c541 Merge pull request #210 from resin-io/jviotti/feature/os-init-type
Make os initialize take a device type instead of a uuid
2015-09-30 15:59:58 -04:00
90be01b05d Make os initialize take a device type instead of a uuid 2015-09-30 14:31:23 -04:00
1124293b9a Merge pull request #209 from resin-io/jviotti/doc/update
Regenerate documentation
2015-09-30 14:30:43 -04:00
0e804fdfd8 Regenerate documentation 2015-09-30 12:07:44 -04:00
0443a35f2b Merge pull request #207 from resin-io/jviotti/fix/promise-using
Fix incorrect Promise.using syntax
2015-09-30 12:06:51 -04:00
3a148217e0 Merge pull request #208 from resin-io/jviotti/fix/manifest-device-type
Send device type correctly to getManifestBySlug
2015-09-30 12:06:45 -04:00
79d1892b66 Send device type correctly to getManifestBySlug
Currently, we we're sending the wholea device object to
`getManifestBySlug`, which ended up in an unsupported device error.
2015-09-30 11:38:34 -04:00
0e06ac464f Fix incorrect Promise.using syntax
`Promise.using` takes the function that acts on the resource as the
second argument, instead of as `.then()`.
2015-09-30 11:37:27 -04:00
5ae83d8337 Merge pull request #206 from resin-io/jviotti/fix/uncompress-os-download
Uncompress zip packages in os download
2015-09-30 10:48:36 -04:00
8694ee2c59 Merge pull request #205 from resin-io/jviotti/feature/rimraf-file-disposer
Use rimraf for deleting os temporary files
2015-09-30 10:31:46 -04:00
8234f7675a Uncompress zip packages in os download
When downloading an operating system image, if the image is a zip
package, uncompress it automatically.
2015-09-30 10:16:24 -04:00
15cb0c4889 Use rimraf for deleting os temporary files
We already use `rimraf` for deleting os temporary directories, however
there are a few benefits of using it for files as well:

- Simplicity. We avoid having to check if a path is a file or directory.
- `rimraf` attempts to workaround the known Windows issues of anti
viruses not closing files. Described in more detail here: https://github.com/resin-io/resin-cli/blob/master/TROUBLESHOOTING.md#i-get-ebusy-errors-after-initializing-a-device-even-as-administrator-on-windows
2015-09-30 10:06:08 -04:00
a3ebd9827f Merge pull request #204 from resin-io/jviotti/fix/console.info
Use console.info in os download
2015-09-30 10:05:19 -04:00
30d84f015a Merge pull request #199 from resin-io/jviotti/feature/197/device-init-apps
Prompt for select application if running device init with no arguments
2015-09-29 15:31:59 -04:00
686414b03d Merge pull request #198 from resin-io/jviotti/feature/temp-disposer
Use Promise.disposer() to make sure temp files are deleted
2015-09-29 15:25:09 -04:00
6377618c12 Use console.info in os download
`console.info` calls can be quieted by the `--quiet` option.
2015-09-29 15:15:39 -04:00
f17e9c97b8 Prompt for select application if running device init with no arguments
Currently, if `device init` was ran without an application argument, we
attempted to get the application name from the current directory, given
it was a git repository.

This approach led to confusions from time to time, so now we prompt the
user to select one of it's own applications from a dropdown instead of
checking the current directory in this edge case.

Fixes: https://github.com/resin-io/resin-cli/issues/197
2015-09-29 15:10:59 -04:00
21fcdfaff6 Use Promise.disposer() to make sure temp files are deleted 2015-09-29 15:08:24 -04:00
4072edcced Merge pull request #203 from resin-io/jviotti/feature/os-initialize
Implement os initialize command
2015-09-29 14:59:59 -04:00
d704c10197 Implement os initialize command
This command initialized an operating system image with a disk device.
2015-09-29 14:52:34 -04:00
dea2a7f055 Merge pull request #202 from resin-io/jviotti/feature/device-register
Implement device register command
2015-09-29 14:41:03 -04:00
7e6eb4b9e4 Implement device register command
This command registers a new device with the passed application,
returning the new device uuid.
2015-09-29 14:33:31 -04:00
61474fba5c Merge pull request #201 from resin-io/jviotti/feature/os-configure
Implement os configure
2015-09-29 14:26:03 -04:00
42256384be Implement os configure
This command, given a path to an image and a device uuid, perform
configuration based on the resin-device-type manifests.
2015-09-29 13:47:10 -04:00
e25f232fe5 Merge pull request #200 from resin-io/jviotti/feature/os-download
Implement os download command
2015-09-29 13:11:24 -04:00
f6d8f12ba2 Implement os download command
This command download an unconfigured image to both the cache and to the
specified location by the `--output` option.
2015-09-29 13:03:14 -04:00
d2b9e6fd8c Merge branch 'jviotti/doc/anti-virus-ebusy' 2015-09-21 09:28:13 -04:00
cf7effacc0 Document anti virus and EBUSY errors on Windows 2015-09-21 09:27:18 -04:00
1eb7aba293 Merge branch 'master' of https://github.com/resin-io/resin-cli 2015-09-21 09:25:57 -04:00
21e916679c Update author email to @resin.io 2015-09-21 09:15:27 -04:00
d574743c21 Merge pull request #195 from resin-io/jviotti/docs/cygwin-to-troubleshooting
Move Cygwin caveat from README to TROUBLESHOOTING
2015-09-21 08:57:39 -04:00
d8a2b82662 Move Cygwin caveat from README to TROUBLESHOOTING 2015-09-21 08:51:54 -04:00
f4ebd890df Merge pull request #194 from resin-io/jviotti/doc/troubleshooting
Add Troubleshooting guide
2015-09-21 08:47:14 -04:00
b0be5de83a Add Troubleshooting guide
This guide described common Resin CLI issues and how to fix them.
2015-09-21 08:32:11 -04:00
ba21ddd010 Merge pull request #192 from resin-io/jviotti/doc/readme
Improve README.md
2015-09-21 08:01:44 -04:00
eecd7a0fd8 Improve README.md
- Make user oriented instead of developer oriented.
- Remove deprecated information.
- Add Requisites section.
- Add Getting Started section.
- Add Support section.
- Add License section.
2015-09-21 07:56:24 -04:00
eb4c2f62a7 Resin CLI v1.0.0 2015-09-11 21:21:44 +03:00
dc1e5e6512 Merge pull request #190 from resin-io/jviotti/message/await
Improve device awaiting message
2015-09-11 19:19:14 +03:00
adc0b183cd Improve device awaiting message
Current message sounds too robotic.
2015-09-11 19:13:30 +03:00
828b4f73d1 Fix selecting existing application in quickstart 2015-09-11 18:30:30 +03:00
82a0761f49 Merge pull request #189 from resin-io/jviotti/feature/error-highlight
Highlight errors in red
2015-09-11 14:59:14 +03:00
904b9f07fb Highlight errors in red
- Move error translation logic to resin-io/resin-cli-errors.
- Force `process.exit()`.
2015-09-11 14:47:38 +03:00
3c0acaa7da Merge pull request #188 from resin-io/jviotti/feature/device-specs
Implement device specs. Fix #99
2015-09-11 13:10:44 +03:00
64c8420c9d Implement device specs. Fix #99
Support for all devices. Tested in the following ones:

- Intel Edison.
- Raspberry Pi 2.
- Parallella.
2015-09-11 13:02:59 +03:00
26e3dc1aa7 Merge pull request #187 from resin-io/jviotti/feature/ignore-resinrc
Add resinrc.yml to gitignore
2015-09-08 09:31:40 +03:00
3bc22a7b7c Merge pull request #186 from resin-io/jviotti/refactor/settings
Use settings from the SDK during login
2015-09-08 09:16:05 +03:00
7e5b2695fe Add resinrc.yml to gitignore
Some modules have an use case for shipping the configuration file with
the project, however this is not the case for the CLI.
2015-09-08 09:07:48 +03:00
79afa79fd9 Use settings from the SDK during login
This enforces all clients to use the Resin Settings Client version that
the SDK provides, reducing incompatibilities caused by different modules
requiring different Resin Settings Client versions.
2015-09-08 09:06:03 +03:00
cdaaddb826 Merge pull request #185 from resin-io/jviotti/upgrade/sdk
Upgrade Resin SDK to v2.7.2
2015-09-07 11:45:25 +03:00
ec5f6a7cd8 Upgrade Resin SDK to v2.7.2 2015-09-07 11:25:01 +03:00
72f34031a9 Merge pull request #184 from resin-io/jviotti/fix/token-auth
Check token validity against the API when login
2015-09-05 20:48:22 +03:00
dc257b5cab Check token validity against the API when login
Consider the following case:

The SDK is configured to point to staging, but the user passes a token
from production, or viceversa. Since the token is valid in a sense that
is valid JWT and contains real data, the CLI will report as a success.

The user will then get Unauthorized errors when using the API.
2015-09-05 20:17:34 +03:00
4bdcd3d2ee Merge pull request #180 from resin-io/issue_#103
Resin CLI Events integration. Fix #103
2015-09-05 20:11:52 +03:00
b0650530cc Resin CLI Events integration. Fix #103 2015-09-05 19:15:31 +03:00
454669ada2 Merge pull request #183 from resin-io/jviotti/182/docs/update
Update documentation
2015-09-01 13:09:42 -04:00
a65975596e Merge pull request #181 from resin-io/reword-example
Replace device name with uuid in env-variables command example
2015-09-01 13:03:27 -04:00
91f878cfc0 Update documentation
- There were changes to commands.
- Regenerates `login` documentation with production url.

Fixes: https://github.com/resin-io/resin-cli/issues/182
2015-09-01 13:01:47 -04:00
8c3e832cdc Replace device name with uuid in env-variables command example 2015-09-01 00:56:01 +03:00
a5c670902d Merge pull request #179 from resin-io/jviotti/appveyor/cache
Add Appveyor cache support
2015-08-28 08:42:57 -04:00
14be1d5f9f Add Appveyor cache support 2015-08-28 08:22:26 -04:00
41d6d5c670 Merge pull request #178 from resin-io/jviotti/refactor/quickstart
Refactor quickstart
2015-08-27 10:13:56 -04:00
a090e6c21d Refactor quickstart
- Use promises.
- Move some logic to `helpers`.
- Inline `device await` command.
2015-08-27 10:01:33 -04:00
d78c1ffa47 Merge pull request #176 from resin-io/jviotti/cleanup/compiled-files
Remove orphaned files from build/
2015-08-27 09:03:38 -04:00
5808d20d88 Merge pull request #177 from resin-io/modify-capitanodoc
Update capitanodoc.json
2015-08-27 15:45:05 +03:00
7f4065e3da Update capitanodoc.json 2015-08-27 15:30:38 +03:00
07daa51051 Remove orphaned files from build/
Some files were deleted from `lib/` but still live in `build/`. More
specifically:

- `build/actions/update.js`.
- `build/data/`.
2015-08-27 08:29:22 -04:00
d10d4ce185 Merge pull request #174 from resin-io/jviotti/feature/confirm-abortion
Add `Aborted` error message when not accepting a confirmation
2015-08-24 08:47:29 -04:00
7f763e7881 Merge pull request #175 from resin-io/jviotti/fix/update-root
Don't check for available updates when running as root
2015-08-24 07:41:56 -04:00
5de0f66d7a Don't check for available updates when running as root
`update-notifier` persist its update check results in a file, which is
then read when running again the application.

If this file gets written when the application is being run as root, we
get ugly EPERM issues.
2015-08-20 16:54:22 -04:00
354921ca92 Add Aborted error message when not accepting a confirmation
This prevents a lot of duplicate code to check for confirmation status
and exit from the current action.
2015-08-20 16:16:20 -04:00
fb28cc7d2f Merge pull request #173 from resin-io/jviotti/upgrade/form
Upgrade Resin CLI Form to v1.2.1
2015-08-20 16:11:17 -04:00
7cf89a9bf6 Merge pull request #172 from resin-io/jviotti/cleanup/readme
Remove Man pages section from README.md
2015-08-20 16:11:09 -04:00
a3cbc549d8 Upgrade Resin CLI Form to v1.2.1
This version contains a fix for a bug that prevented `when` properties
from working as expected.
2015-08-20 15:55:57 -04:00
1c48328347 Remove Man pages section from README.md
Man pages are not longer being produced.
2015-08-20 12:04:11 -04:00
9b69fe3c3c Merge pull request #171 from resin-io/jviotti/upgrade/cli-form
Update Resin CLI Form to v1.2.0
2015-08-20 12:02:26 -04:00
dc513a08f6 Update Resin CLI Form to v1.2.0
This version includes support for the `drive` input type.
2015-08-20 11:55:53 -04:00
fcc44949a7 Merge pull request #169 from resin-io/jviotti/refactor/plugins
Upgrade Nplugm to v3.0.0
2015-08-19 12:49:32 -04:00
006764af66 Merge pull request #170 from resin-io/jviotti/cleanup-resin-write
Remove unused resin-write bin script
2015-08-19 12:49:25 -04:00
b879d3f9ea Remove unused resin-write bin script
This script was used along with windosu as a workaround to call `device
init` with elevated permissions.

Since Windows elevation is not used anymore for now, this script can be
removed.
2015-08-19 11:30:48 -04:00
7f4863da86 Upgrade Nplugm to v3.0.0
This new version supports promises and contains speed improvements.
2015-08-19 11:27:28 -04:00
ba6f50d171 Merge pull request #168 from resin-io/jviotti/cleanup/plugins
Remove plugins manipulation commands
2015-08-19 11:14:35 -04:00
a803d4f646 Remove plugins manipulation commands
Since we're now forcing users to rely on `npm` directly for updates, we
can also get rid of plugin commands that attempt to
install/update/remove using npm programatically and require users to use
`npm` directly as well.

This commit removes the following commands:

- `plugins`
- `plugin install`
- `plugin update`
- `plugin remove`

Despite plugin related commands being removed, *the functionality that
scans for plugins and registers them remains intact*.
2015-08-19 10:57:42 -04:00
85d940df66 Merge pull request #165 from resin-io/jviotti/feature/update-notifier
Notify the user if there is an available update
2015-08-19 07:56:40 -04:00
6f9535ca34 Merge pull request #167 from resin-io/issue-166
Display msg when app/device does not have env variables. Fix #166.
2015-08-18 18:17:21 +03:00
019e2ac357 Display msg when app/device does not have env variables. Fix #166 2015-08-18 18:12:08 +03:00
433916e18a Merge pull request #164 from resin-io/issue-90
Add message informing the user about potential delay in system img initialization. Fix #90.
2015-08-18 16:04:44 +03:00
f19588032f Notify the user if there is an available update
For this we use the `update-notifier` module with its default settings.

This module will print a nice banner prompting the user to run the
corresponding npm command to update.
2015-08-18 08:53:06 -04:00
0595452c3d Add message informing the user about potential delay in system img initialization. Fix #90. 2015-08-18 15:43:52 +03:00
d6305df48e Merge pull request #163 from resin-io/issue-108
Reword ending message in quickstart. Fix #108
2015-08-18 15:00:38 +03:00
f7084580b2 Merge pull request #162 from resin-io/issue-106
Reword output during download in device init. Fix #106.
2015-08-18 14:59:08 +03:00
3dd5f5858a Reword ending message in quickstart. Fix #108 2015-08-18 14:34:15 +03:00
02a06e1e7c Reword output during download in device init. Fix #106. 2015-08-18 13:55:04 +03:00
50daf8ef73 Merge pull request #161 from resin-io/jviotti/refactor/env
Refactor env action module to use promises
2015-08-17 10:42:40 -04:00
fd5a34a1c4 Refactor env action module to use promises 2015-08-17 10:32:22 -04:00
51fda13684 Merge pull request #160 from resin-io/jviotti/remove/devices-supported
Remove `devices supported` command
2015-08-17 10:22:10 -04:00
a698b25fda Remove devices supported command
The command is not necessary and unused.
2015-08-17 10:05:36 -04:00
89bd861d8e Merge pull request #159 from resin-io/jviotti/refactor/device
Refactor device actions to use promises
2015-08-17 10:03:57 -04:00
e5b7aae4ae Refactor device actions to use promises 2015-08-17 09:49:59 -04:00
031168ceed Merge pull request #158 from resin-io/jviotti/refactor/keys
Refactor keys action to use promises
2015-08-17 09:46:53 -04:00
09a5788902 Refactor keys action to use promises 2015-08-17 09:32:05 -04:00
713664d103 Merge pull request #157 from resin-io/jviotti/feature/settings-projects-dir
Make use of `projectsDirectory` SDK setting in Quickstart
2015-08-17 09:20:30 -04:00
f63391acf9 Make use of projectsDirectory SDK setting in Quickstart
We were currently building this path ourselves, hardcoding the place of
the resin local per user directory instead of relying on the foundations
that `resin-settings-client` give us.
2015-08-17 09:06:27 -04:00
79ee4302ec Merge pull request #156 from resin-io/jviotti/upgrade/sdk
Upgrade Resin SDK to v2.4.1
2015-08-17 08:59:17 -04:00
9adda22921 Upgrade Resin SDK to v2.4.1
This new version contains fixes for the following issues:

- https://github.com/resin-io/resin-cli/issues/87
- https://github.com/resin-io/resin-cli/issues/120
2015-08-17 08:42:17 -04:00
25311f2a18 Merge pull request #146 from resin-io/jviotti/refactor/auth
Refactor auth actions to use promises
2015-08-17 08:41:19 -04:00
70c060b124 Refactor auth actions to use promises 2015-08-17 08:22:48 -04:00
7a8a3c851b Merge pull request #138 from resin-io/refactor/help
Refactor help module
2015-08-17 08:02:15 -04:00
1096b2d212 Merge pull request #143 from resin-io/jviotti/refactor/app
Refactor app actions to use promises
2015-08-17 08:02:04 -04:00
ee286c5690 Merge pull request #144 from resin-io/jviotti/refactor/note
Refactor note set command to use promises
2015-08-17 08:01:47 -04:00
1da1d2e6fc Merge pull request #152 from resin-io/jviotti/fix/151/ssh-key-list
Print ssh key separately from the information table
2015-08-17 08:01:32 -04:00
64be9f936d Merge pull request #155 from resin-io/jviotti/feature/107/await-spinner
Implement a spinner when awaiting for a device. Fix #107
2015-08-17 08:01:22 -04:00
30f24333c0 Implement a spinner when awaiting for a device. Fix #107
Fixes:

- https://github.com/resin-io/resin-cli/issues/107
2015-08-14 14:35:38 -04:00
30f6a78282 Merge pull request #154 from resin-io/jviotti/fix/device-await
Fix broken device await command
2015-08-14 14:31:00 -04:00
8c9a0e0ff1 Fix broken device await command
There were two issues that prevented this command from working
correctly:

1- `Promise.delay()` is used, but `Promise` was not imported.
2- The following line had incorrect indentation (spaces instead of
		tabs):

		poll().nodeify(done)

Therefore CoffeeScript interpreted that the line had to be executed at
the end of the `poll()` function, causing `poll()` to never be called.
2015-08-14 14:11:49 -04:00
8268bbf700 Merge pull request #150 from resin-io/jviotti/upgrade/sdk
Upgrade Resin SDK to v2.4.0
2015-08-14 12:42:31 -04:00
e712e2f266 Print ssh key separately from the information table
Since the public key string is long, it might wrap to lines below,
causing the table layout to break.

A quick solutio is to print the ssh key after the table.

Fixes:

- https://github.com/resin-io/resin-cli/issues/151
2015-08-14 12:25:55 -04:00
0807b6a2d9 Upgrade Resin SDK to v2.4.0
This release fixes:

- Check if device exists before removing it "resin device rm <uuid>"
	- https://github.com/resin-io/resin-cli/issues/123

- Check if app exists before removing it "resin app rm <appName>"
	- https://github.com/resin-io/resin-cli/issues/114

- Command does not display correct output "resin key <id>"
	- https://github.com/resin-io/resin-cli/issues/112

Since it includes the following PRs:

- https://github.com/resin-io/resin-sdk/pull/103
- https://github.com/resin-io/resin-sdk/pull/107
2015-08-14 12:17:59 -04:00
83382cc8f7 Merge pull request #145 from resin-io/jviotti/fix/logs-help-indentation
Fix logs command help string indentation
2015-08-14 08:27:38 -04:00
8401aaeae2 Merge pull request #149 from resin-io/jviotti/fix/111/email-validation
Validate that email address is valid during signup. Fix #111
2015-08-14 07:58:40 -04:00
abf5740950 Merge pull request #148 from resin-io/jviotti/fix/14/logs-history
Force logs command to exit when not in --tail mode. Fix #14.
2015-08-14 07:58:05 -04:00
606777508d Merge pull request #147 from resin-io/jviotti/cleanup/preferences
Remove preferences command
2015-08-14 07:57:14 -04:00
e9ec6c67b2 Validate that email address is valid during signup. Fix #111
For this we use a third party dependency from npm called `valid-email`
to avoid hardcoding and having to mantain a regular expression.
2015-08-13 15:22:22 -04:00
69566f7fc3 Force logs command to exit when not in --tail mode. Fix #14.
PubNub keeps the process alive after a history query for some reason, so
trying to print the logs history like:

	$ resin logs <uuid>

Will result in the logs being printed correctly, but the process waiting
infinitely without ending.

The workaround consists in forcing `process.exit` to exit the process
with an error code zero.

Caveats:

- This workaround prevents this command to be used programatically.

Issue: https://github.com/resin-io/resin-cli/issues/14
2015-08-13 15:08:16 -04:00
6e4b299c7d Remove preferences command 2015-08-13 15:00:51 -04:00
ef35ebf79d Fix logs command help string indentation
For some reason it was indented a few times unnecesarily.
2015-08-13 14:33:19 -04:00
1bc78edf71 Refactor help module
Main changes:

- Use the `columnify` module to display the commands instead of using
manual parsing.

- Extract logic to create a string representation from an option
signature to Capitano, and reuse here.

See https://github.com/resin-io/capitano/pull/28

Some bugs were caught and fixes during the refactoring:

- In command help, if the command didn't exist, we reused default
Capitanos command not found function which uses `process.exit(1)`. This
was changed to pass a custom error to `done()`, so the command fails
correctly when using programatically.

- General help didn't call `done()` at all, thus causing problems if
using the command programatically someday.
2015-08-13 14:19:07 -04:00
d5204a09f7 Refactor note set command to use promises 2015-08-13 14:17:02 -04:00
4647aa70c0 Implement utils/helpers to abstract common app patterns
- Add helpers.confirm() to abstract the process of asking for
confirmation.
- Add helpers.selectDeviceType() to abstract the form needed to ask for
device types.

The functions on this module are reused by app actions.
2015-08-13 14:04:47 -04:00
2e8ec3ac64 Merge pull request #142 from resin-io/jviotti/cleanup/dependencies
Remove unused dependencies imports from various files
2015-08-13 13:51:22 -04:00
25c6246e9f Refactor app actions to use promises
Use promises instead of `async` internally inside the following
commands:

- app create.
- app remove.
- app associate.
2015-08-13 13:42:49 -04:00
5408938007 Merge pull request #141 from resin-io/jviotti/cleanup/shell-completion
Remove command shell completion script
2015-08-13 13:08:08 -04:00
50cb04b6f7 Remove unused dependencies imports from various files 2015-08-13 13:04:22 -04:00
09fe4b11ad Merge pull request #139 from resin-io/jviotti/feature/drive-widget
Use Visuals drive widget in device init
2015-08-13 12:35:41 -04:00
4157f21e06 Merge pull request #140 from resin-io/jviotti/cleanup/elevate
Remove outdated Windows elevation mechanism
2015-08-13 12:23:26 -04:00
4900230881 Remove command shell completion script
This feature is unused and doesn't provide much value.

In the future, we may include autocompletion support built in the CLI.
2015-08-13 12:21:55 -04:00
e60c0605e5 Use Visuals drive widget in device init
- Replace custom `drivelist` logic in "device init" with the new `drive`
widget.
2015-08-13 11:56:16 -04:00
085781fa18 Upgrade Resin CLI Visuals to v1.1.0
This version contains the `drive` widget.
2015-08-13 11:55:31 -04:00
08ad8bd5d7 Merge pull request #137 from resin-io/cleanup/slim
Slim down unused functionality
2015-08-13 11:52:57 -04:00
3d36e5f5d3 Remove outdated Windows elevation mechanism
This functionality is outdated and not using anymore due to limitations
in the way it was addressed.

The module and dependencies are removed for now, and will be added back
in the future, once a better approach is planned.
2015-08-13 11:45:50 -04:00
57319f26a6 Slim down unused functionality 2015-08-12 08:17:46 -04:00
11033683fd Stop supporting iojs 2015-08-06 12:12:44 -04:00
e5166c6c8e Use Install-Product instead of Update-NodeJsInstallation 2015-08-06 11:46:57 -04:00
5c96663d1e Merge pull request #134 from resin-io/remove/drive-command
Remove drive command
2015-08-05 12:24:25 -04:00
ffb48c8669 Merge pull request #135 from resin-io/remove/examples-commands
Remove examples commands
2015-08-05 07:04:03 -04:00
12145a2393 Merge pull request #132 from resin-io/upgrade_travis
Test node v0.12 and io.js
2015-08-05 07:03:51 -04:00
f379866c1c Merge pull request #67 from resin-io/feature/wizard
Implement Quickstart command
2015-08-04 13:19:37 -04:00
dc030f4cd1 Implement Quickstart command 2015-08-04 20:16:55 +03:00
b726a2d778 Remove examples commands 2015-08-04 10:00:09 -04:00
a715ec9dc1 Remove drive command 2015-08-04 09:57:59 -04:00
d24b871964 Merge pull request #131 from resin-io/remove/selfupdate
Remove selfupdate functionality
2015-08-04 09:53:48 -04:00
bd0c4e6034 Merge pull request #133 from resin-io/upgrade_appveyor
Add io.js testing in Appveyor
2015-08-04 09:27:36 -04:00
95637e5608 Test node v0.12 and io.js 2015-08-04 14:15:30 +03:00
93b394ed76 Add io.js testing in Appveyor 2015-08-04 13:26:13 +03:00
b515e427ff Merge pull request #129 from resin-io/issue_#73
Add email address to the returned information, when using whoami(). Fix #73.
2015-08-03 16:14:47 -04:00
26b1acf5ef Merge pull request #130 from resin-io/remove/man
Remove man pages
2015-08-03 16:14:23 -04:00
f31eb7c2b5 Add email address to the returned information, when using whoami(). Fix #73. 2015-08-03 21:24:22 +03:00
d423a6ea24 Remove selfupdate functionality
We added this because we thought that knowledge of the supported device types, along with the configuration procedures was going to be encoded in the CLI.

With device specs, this is not longer the case.
2015-08-03 12:20:42 -04:00
4211333e4e Remove man pages 2015-08-03 12:08:49 -04:00
f220e380a7 Merge pull request #125 from resin-io/issue_#117
Display correctly the newly-created application id. Fix #117
2015-07-29 21:43:31 +03:00
9564b4e478 Display correctly the newly-created application id. Fix #117 2015-07-29 21:17:21 +03:00
e2125b8ce9 Fix #73 2015-07-29 21:15:29 +03:00
0bb0e6ea4b Merge pull request #124 from resin-io/integrate-new-resin-visuals-functionality
Integrate new resin-cli-visuals functionality
2015-07-29 09:43:24 -04:00
cf512cc01b Integrate new resin-cli-visuals functionality 2015-07-29 16:34:31 +03:00
fe3e68c3af Merge pull request #121 from resin-io/modify-function-description
Replace device name with uuid, found in resin envs examples in enviroment-variables
2015-07-28 08:49:19 -04:00
0bbfbe36c7 Replace device name with uuid, found in resin envs examples in enviroment-variables 2015-07-27 22:49:11 +03:00
d6ae689593 Merge pull request #118 from resin-io/resin-cli-form
Integrate resin-cli-form
2015-07-27 13:30:48 -04:00
5b5d1be52f Integrate resin-cli-form 2015-07-27 19:50:47 +03:00
cb808869dd Merge pull request #110 from resin-io/support_promises
Add promise support for Resin-SDK dependency
2015-07-24 08:02:40 -04:00
64d83dccfb Add promise support for Resin-SDK dependency 2015-07-24 00:24:17 +03:00
122253bb25 Merge pull request #95 from resin-io/fix/device-registered-at
Add registered_at UNIX epoch
2015-07-23 13:00:29 -04:00
1d53db2854 Add registered_at UNIX epoch 2015-07-23 12:47:49 -04:00
30dc5ea1ea Merge pull request #97 from resin-io/upgrade/dependencies
Upgrade dependencies
2015-07-15 09:08:35 -04:00
58c7ff1f1b Upgrade dependencies 2015-07-15 09:01:50 -04:00
57b9d634be Merge pull request #96 from resin-io/upgrade-visuals
Upgrade resin-cli-visuals dependency
2015-07-14 15:12:54 -04:00
6fb25dea88 Upgrade resin-cli-visuals dependencies 2015-07-14 18:18:19 +03:00
10478e389a Merge pull request #94 from resin-io/upgrade_vcs_dependencies
Upgrade Resin VCS dependencies
2015-07-13 07:37:02 -04:00
46fa4ee2a2 Upgrade Resin VCS dependencies 2015-07-11 00:03:20 +03:00
a7cefe44bf Merge pull request #86 from resin-io/feature/remove-selfupdate
Remove selfupdate functionality. Notify in all cases.
2015-07-09 13:45:09 -04:00
3d03585318 Merge pull request #93 from resin-io/feature/last_seen-undefined
Default device.last_seen to 'Not seen'. Closes #84.
2015-07-09 13:44:59 -04:00
076c3428ee Upgrade Resin CLI Visuals to v0.3.2 2015-07-09 13:43:20 -04:00
9d4ac46985 Default device.last_seen to 'Not seen'. Closes #84. 2015-07-09 09:56:39 -04:00
24edb867fa Upgrade Resin CLI Visuals to v0.3.1 2015-07-09 09:46:08 -04:00
e926ac46c9 Remove selfupdate functionality. Notify in all cases. 2015-07-09 08:18:06 -04:00
9ffd657dbb Merge pull request #85 from resin-io/feature/regenerate-docs
Regenerate docs
2015-07-07 18:40:40 -04:00
aa3cb39551 Regenerate docs 2015-07-07 18:01:25 -04:00
0acbdac66f Use NPM 2.12.1 in Appveyor due to a bug in 2.12.0 2015-07-02 14:28:19 -04:00
5619bdbb67 Resin CLI v0.11.1 2015-06-15 10:09:10 -04:00
381e63bfc9 Merge pull request #70 from resin-io/refactor/new-visuals
Upgrade Resin CLI Visuals and use it's new capabilities
2015-06-12 09:47:30 -04:00
e779347ff2 Merge pull request #69 from resin-io/feature/no-chop-key
Don't chop SSH key
2015-06-11 12:48:16 -04:00
8fa906dd48 Upgrade Resin CLI Visuals and use it's new capabilities 2015-06-11 12:46:56 -04:00
7e5ecd634d HOTFIX: isLoggedIn now returns a possible error 2015-06-11 12:40:49 -04:00
29cf4c1e89 Don't chop SSH key 2015-06-11 08:08:45 -04:00
73736abdea Merge pull request #68 from resin-io/feature/config
Implement config command
2015-06-11 07:57:31 -04:00
9ab411bade Resin CLI v0.11.0 2015-06-10 12:52:28 -04:00
ef33156de7 Implement config command 2015-06-10 12:34:42 -04:00
6d8fd6e547 Merge pull request #66 from resin-io/feature/device-init-has-app
Check that the passed application exists before asking to choose device
2015-06-09 08:28:10 -04:00
bab90a8bf2 Merge pull request #65 from resin-io/fix/app-rm-help
Fix app commands order of definition. Closes #62.
2015-06-08 12:44:57 -04:00
43f0288c6c Check that the passed application exists before asking to choose device 2015-06-08 12:31:17 -04:00
47e6371e2e Merge pull request #64 from resin-io/feature/config-inject
Implement config injection
2015-06-05 12:06:54 -04:00
5e400ed335 Fix app commands order of definition. Closes #62.
This caused `resin help app rm` erroneusly show the help page for `resin app`.
2015-06-04 11:54:15 -04:00
56a78b57af Merge branch 'master' of https://github.com/resin-io/resin-cli 2015-06-04 10:38:53 -04:00
2bfeb7f42c Associate a device before first boot 2015-06-04 10:10:15 -04:00
37e0f12f89 Merge pull request #57 from resin-io/feature/associate-confirmation
Ask for confirmation on app associate command
2015-06-04 08:22:01 -04:00
fdd0e4a966 Implement config injection 2015-06-04 08:06:37 -04:00
38df7e0038 Use min mocha reporter 2015-06-04 08:06:08 -04:00
e9efb78280 Merge pull request #56 from resin-io/feature/login-feedback
Display feedback message after login
2015-06-03 08:46:18 -04:00
0424d7b640 Merge pull request #60 from resin-io/fix/login-browser-session
Open dashboardUrl instead of remoteUrl in auth login
2015-06-03 08:45:00 -04:00
2b80c7c91f Merge pull request #59 from resin-io/feature/app-create-feedback
Print feedback message after app create
2015-06-02 17:08:44 -04:00
142e2c0a65 Merge pull request #58 from resin-io/feature/tty-feedback
Quiet console.info if stdout is being redirected
2015-06-02 17:08:31 -04:00
1ed9ae7d60 Open dashboardUrl instead of remoteUrl in auth login 2015-06-02 13:21:59 -04:00
329bf25dbd Print feedback message after app create 2015-06-02 13:04:08 -04:00
e18ffba183 Quiet console.info if stdout is being redirected
We use `console.info()` for feedback messages.
2015-06-02 12:36:17 -04:00
ae3f0b429d Ask for confirmacion on app associate command 2015-06-02 12:32:35 -04:00
34736c4e9b Display feedback message after login 2015-06-02 11:57:52 -04:00
806678ee5f Merge pull request #55 from resin-io/feature/device-env-vars
Add per device environment variable support
2015-06-02 08:13:10 -04:00
1e70401bdd Make use of NPM@2 in Appveyor to prevent lock permission issues 2015-06-01 08:46:19 -04:00
3c7615f20d Upgrade drivelist to v1.2.2
Contains isSystem() fix for Linux
2015-06-01 08:35:48 -04:00
054d5e4879 Add per device environment variable support 2015-05-28 12:32:08 -04:00
b04ed43bed Merge pull request #54 from resin-io/fix/device-await-help
Fix device await help not showing up
2015-05-28 08:46:42 -04:00
c2bbb952c5 Fix device await help not showing up 2015-05-27 15:14:36 -04:00
63a9e1859b Merge pull request #53 from resin-io/feature/example-name
Accept names instead of ids in example commands
2015-05-25 08:53:06 -04:00
68ef069e6a Accept names instead of ids in example commands 2015-05-21 11:58:15 -04:00
72d4b21cb4 Configure appveyor CI 2015-05-21 10:52:37 -04:00
e8b931680d Merge pull request #52 from resin-io/fix/remove-test-notifier
Remove Mocha notification integration
2015-05-20 13:50:47 -04:00
218b407f30 Remove Mocha notification integration
It doesn't work in tmux and causes the process to hang.
2015-05-20 13:15:39 -04:00
773cc27145 Resin CLI v0.10.8 2015-05-19 15:04:52 -04:00
1c76e2e15b Upgrade Resin Image to v1.1.3 2015-05-19 15:02:12 -04:00
e466cfd6ff Resin CLI v0.10.7 2015-05-18 10:39:07 -04:00
992b04d521 Merge pull request #51 from resin-io/feature/nicer-logs
Expose logs as cli.actions.logs instead of cli.actions.logs.logs
2015-05-18 10:38:41 -04:00
5c16c86871 Merge pull request #50 from resin-io/fix/js-main
Point to a JavaScript file in package.json main property
2015-05-18 10:38:28 -04:00
78af9bbb10 Expose logs as cli.actions.logs instead of cli.actions.logs.logs 2015-05-18 09:37:27 -04:00
b78c48d89f Point to a JavaScript file in package.json main property 2015-05-18 09:35:10 -04:00
1f8c610bb0 Resin CLI v0.10.6 2015-05-14 11:50:43 -03:00
d220728380 Merge pull request #49 from resin-io/feature/device-list-devices
Return devices array to the done callback in device list command
2015-05-14 11:47:25 -03:00
db58e9986c Return devices array to the done callback in device list command
Useful when using the CLI programatically.
2015-05-14 11:43:41 -03:00
2a5e66eca6 Merge pull request #48 from resin-io/feature/use-selfupdate
Make use of selfupdate
2015-05-14 11:40:23 -03:00
e7e8ec296c Make use of selfupdate 2015-05-14 11:32:18 -03:00
80e69c56d0 Resin CLI v0.10.5 2015-05-11 14:42:00 -03:00
ce3c6aecff Upgrade Resin SDK to v1.4.3 2015-05-11 14:39:55 -03:00
300f76ba83 Upgrade Resin SDK to v1.4.2 2015-05-11 14:27:09 -03:00
a4e8a38bf4 Merge pull request #44 from resin-io/fix/download-exit
Fix callback not being called on success in os download command
2015-05-11 14:26:07 -03:00
9bb04f43a8 Fix callback not being called on success in os download command 2015-05-11 14:23:34 -03:00
9b0c08bd46 Don't umount in os install as it's already handled in Resin Image 2015-05-11 13:10:06 -03:00
679d48e86e Merge branch 'master' of github.com:resin-io/resin-cli 2015-05-11 13:08:44 -03:00
4b6bf60531 Upgrade Resin Image to v1.1.2 2015-05-11 12:38:19 -03:00
d8ce6648e2 Handle device unmounting in os install command 2015-05-11 10:04:05 -03:00
402bc2d3a8 Merge pull request #43 from resin-io/fix/non-interactive-login
Make login command behave non interactively if a token is passed as an argument
2015-05-11 09:46:07 -03:00
f3e193be0f Make login command behave non interactively if a token is passed as an argument 2015-05-11 09:42:25 -03:00
f5b461612b Upgrade Capitano to v1.6.0 2015-05-11 09:30:07 -03:00
d2514a3fc3 Resin CLI v0.10.4 2015-05-08 14:29:02 -03:00
2019f9fdeb Merge pull request #42 from resin-io/feature/use-resin-image
Make use of resin-image to remove image writing logic from the CLI
2015-05-08 14:28:33 -03:00
656f3e5cd9 Make use of resin-image to remove image writing logic from the CLI 2015-05-08 14:26:04 -03:00
741acfbba3 Merge pull request #41 from resin-io/feature/command-run
Make use of capitano.run() to run subcommands
2015-05-08 14:23:37 -03:00
4b7eca02a0 Resin CLI v0.10.3 2015-05-07 12:03:40 -03:00
c7788a80e1 Upgrade Resin SDK to v1.4.1
That version includes the os download piping issue fix
2015-05-07 12:03:12 -03:00
08648894e3 Merge pull request #40 from resin-io/feature/device-await
Implement device await command
2015-05-06 11:18:42 -03:00
4c5d5697bc Implement device await command 2015-05-06 11:14:08 -03:00
c758a5b9ea Make use of capitano.run() to run subcommands 2015-05-05 17:34:24 -03:00
2af54e2e28 Merge pull request #39 from resin-io/feature/coffee-compile
Compile CoffeeScript by default when initializing watch task
2015-05-05 06:49:45 -03:00
cf9b23b987 Compile CoffeeScript by default when initializing watch task 2015-05-05 00:21:30 -04:00
f8380ec8e6 Merge pull request #38 from resin-io/feature/os-install-umount
Handle device unmounting in os install command
2015-05-05 00:06:25 -04:00
cd3245a631 Handle device unmounting in os install command 2015-05-02 10:34:11 -04:00
144a155208 Merge pull request #36 from resin-io/feature/standard-version
Implement --version/-v global options. Closes #35
2015-04-30 13:12:51 -04:00
7f7ca13001 Implement --version/-v global options. Closes #35 2015-04-30 11:56:15 -04:00
361e149063 Merge pull request #34 from resin-io/fix/note-validation
Fix note command validation issues
2015-04-29 14:43:47 -04:00
fab85b381a Throw a nice error if note contents are missing 2015-04-29 14:39:46 -04:00
faecf103bf Upgrade Resin SDK to v1.3.4
This version addresses the device validation issue
2015-04-29 14:39:28 -04:00
a0f8c1aa8c Merge pull request #33 from resin-io/fix/devices-app-name
Fix devices command not showing application names. Closes #32
2015-04-29 14:13:09 -04:00
24726b575d Fix devices command not showing application names. Closes #32 2015-04-29 14:07:59 -04:00
688b89a099 Resin CLI v0.10.2 2015-04-28 09:34:41 -04:00
2e39110cc0 Merge pull request #31 from resin-io/feature/app-uniqueness
Check that an application exists before asking it's type. Closes #30
2015-04-28 09:33:59 -04:00
193cedae26 Check that an application exists before asking it's type. Closes #30 2015-04-28 09:18:21 -04:00
3d1f023ef5 Merge pull request #29 from resin-io/feature/list-all-devices
Make devices command list all devices if no application option
2015-04-27 11:26:00 -04:00
9bb51d7146 Make devices command list all devices if no application option. Closes #17 2015-04-27 11:21:30 -04:00
84900aa588 Remove outdated uuid checking help message 2015-04-27 10:51:26 -04:00
b2bce3c4ac Upgrade Resin SDK 2015-04-27 10:49:28 -04:00
8c11620a42 Upgrade Resin SDK 2015-04-27 10:33:34 -04:00
7c1e0ac2e9 Merge pull request #28 from resin-io/fix/logs-messages
Fix logs printing, as messages got added a message property
2015-04-27 10:05:16 -04:00
9a3c844336 Print logs correctly, as they recently got a message field added 2015-04-27 09:30:57 -04:00
69039d21c2 Resin CLI v0.10.1 2015-04-21 11:28:24 -04:00
34c9cdd8bb Upgrade Resin SDK to v1.2.1 2015-04-21 11:26:40 -04:00
0a8b30e824 Resin CLI v0.10.0 2015-04-21 09:06:45 -04:00
ea5b521262 Merge pull request #26 from resin-io/fix/device-init-temp-output
Fix temporary os download output option in device init
2015-04-21 09:06:11 -04:00
3bc71577b5 Fix temporary os download output option in device init 2015-04-21 08:37:40 -04:00
0fb24162fc Merge pull request #24 from resin-io/fix/update
Fix logical issue in update command
2015-04-20 14:14:33 -04:00
d0d63f5bbb Merge pull request #25 from resin-io/deduplicate/whoami
Remove duplicated whoami command
2015-04-20 14:14:25 -04:00
8041905144 Remove duplicated whoami command 2015-04-20 12:46:43 -04:00
37d96e238d Fix logical issue in update command 2015-04-20 12:42:31 -04:00
ae8c941bfe Resin CLI v0.9.0 2015-04-20 12:15:57 -04:00
4c16835ca4 Merge pull request #23 from resin-io/feature/device-init-app-name
Make device init and os download take app names instead of ids
2015-04-20 12:14:53 -04:00
7bd8922a4e Upgrade Resin VCS to v1.2.0 and make use of vcs.getApplicationName() 2015-04-20 11:41:26 -04:00
42b2e2fcf0 Regenerate documentation 2015-04-20 09:14:47 -04:00
f7256e9927 Make device init command take an application name instead of id 2015-04-20 09:13:15 -04:00
85444a5a6a Make os download command accept an application name instead of id 2015-04-20 09:06:40 -04:00
ac115c7ea3 Merge pull request #22 from resin-io/feature/clone-dir
Make "example clone" clone to a new directory
2015-04-20 08:59:28 -04:00
3c2a440553 Make "example clone" clone to a new directory 2015-04-20 08:56:10 -04:00
172 changed files with 13248 additions and 4469 deletions

View File

@ -9,4 +9,8 @@ trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
trim_trailing_whitespace = false
[package.json]
indent_style = space
indent_size = 2

2
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,2 @@
- **resin-cli version:**
- **Operating system and architecture:**

18
.gitignore vendored
View File

@ -13,9 +13,6 @@ lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
@ -27,9 +24,16 @@ build/Release
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
node_modules
bin/node
release/build
npm-shrinkwrap.json
package-lock.json
.resinconf
resinrc.yml
.idea
.vscode
.DS_Store
/tmp
build/
build-bin/
build-zip/

View File

@ -1,5 +1,5 @@
coffee_script:
config_file: coffeelint.json
java_script:
javascript:
enabled: false

5
.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"singleQuote": true,
"trailingComma": "all",
"useTabs": true
}

View File

@ -1,4 +1,19 @@
language: node_js
os:
- linux
- osx
node_js:
- "0.11"
- "0.10"
- "6"
before_install:
- npm -g install npm@4
script: npm run ci
notifications:
email: false
deploy:
- provider: script
script: npm run release
skip_cleanup: true
on:
tags: true
condition: "$TRAVIS_TAG =~ ^v?[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+"
repo: resin-io/resin-cli

1057
CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

190
LICENSE
View File

@ -1,21 +1,177 @@
The MIT License
Copyright (c) 2014 Resin.io, Inc. https://resin.io
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
1. Definitions.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

133
README.md
View File

@ -1,65 +1,126 @@
# Resin CLI
Resin CLI
=========
> The official resin.io CLI tool.
[![npm version](https://badge.fury.io/js/resin-cli.svg)](http://badge.fury.io/js/resin-cli)
[![dependencies](https://david-dm.org/resin-io/resin-cli.png)](https://david-dm.org/resin-io/resin-cli.png)
[![Build Status](https://travis-ci.org/resin-io/resin-cli.svg?branch=master)](https://travis-ci.org/resin-io/resin-cli)
[![dependencies](https://david-dm.org/resin-io/resin-cli.svg)](https://david-dm.org/resin-io/resin-cli)
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/resin-io/chat)
The official Resin CLI tool.
Requisites
----------
## Installing
If you want to install the CLI directly through npm, you'll need the below. If this looks difficult,
we do now have an experimental standalone binary release available, see ['Standalone install'](#standalone-install) below.
- [NodeJS](https://nodejs.org) (>= v6)
- [Git](https://git-scm.com)
- The following executables should be correctly installed in your shell environment:
- `ssh`: Any recent version of the OpenSSH ssh client (required by `resin sync` and `resin ssh`)
- if you need `ssh` to work behind the proxy you also need [`proxytunnel`](http://proxytunnel.sourceforge.net/) installed (available as `proxytunnel` package for Ubuntu, for example)
- `rsync`: >= 2.6.9 (required by `resin sync`)
##### Windows Support
Before installing resin-cli, you'll need a working node-gyp environment. If you don't already have one you'll see native module build errors during installation. To fix this, run `npm install -g --production windows-build-tools` in an administrator console (available as 'Command Prompt (Admin)' when pressing windows+x in Windows 7+).
`resin sync` and `resin ssh` have not been thoroughly tested on the standard Windows cmd.exe shell. We recommend using bash (or a similar) shell, like Bash for Windows 10 or [Git for Windows](https://git-for-windows.github.io/).
If you still want to use `cmd.exe` you will have to use a package manager like MinGW or chocolatey. For MinGW the steps are:
1. Install [MinGW](http://www.mingw.org).
2. Install the `msys-rsync` and `msys-openssh` packages.
3. Add MinGW to the `%PATH%` if this hasn't been done by the installer already. The location where the binaries are places is usually `C:\MinGW\msys\1.0\bin`, but it can vary if you selected a different location in the installer.
4. Copy your SSH keys to `%homedrive%%homepath\.ssh`.
5. If you need `ssh` to work behind the proxy you also need to install [proxytunnel](http://proxytunnel.sourceforge.net/)
Getting Started
---------------
### NPM install
If you've got all the requirements above, you should be able to install the CLI directly from npm. If not,
or if you have any trouble with this, please try the new standalone install steps just below.
This might require elevated privileges in some environments.
```sh
$ npm install -g resin-cli
$ npm install resin-cli -g --production --unsafe-perm
```
### Running locally
`--unsafe-perm` is only required on systems where the global install directory is not user-writable.
This allows npm install steps to download and save prebuilt native binaries. You may be able to omit it,
especially if you're using a user-managed node install such as [nvm](https://github.com/creationix/nvm).
In some environments, this process will need to build native modules. This may require a more complex build
environment, and notably requires Python 2.7. If you hit any problems with this, we recommend you try the
alternative standalone install below instead.
### Standalone install
If you don't have node or a working pre-gyp environment, you can still install the CLI as a standalone
binary. **This is experimental and may not work perfectly yet in all environments**, but it seems to work
well in initial cross-platform testing, so it may be useful, and we'd love your feedback if you hit any issues.
To install the CLI as a standalone binary:
* Download the latest zip for your OS from https://github.com/resin-io/resin-cli/releases.
* Extract the contents, putting the `resin-cli` folder somewhere appropriate for your system (e.g. `C:/resin-cli`, `/usr/local/lib/resin-cli`, etc).
* Add the `resin-cli` folder to your `PATH` ([Windows instructions](https://www.computerhope.com/issues/ch000549.htm), [Linux instructions](https://stackoverflow.com/questions/14637979/how-to-permanently-set-path-on-linux-unix), [OSX instructions](https://stackoverflow.com/questions/22465332/setting-path-environment-variable-in-osx-permanently))
* Running `resin` in a fresh command line should print the resin CLI help.
To update in future, simply download a new release and replace the extracted folder.
Have any problems, or see any unexpected behaviour? [Please file an issue!](https://github.com/resin-io/resin-cli/issues/new)
### Login
```sh
$ ./bin/resin
$ resin login
```
## Tests
_(Typically useful, but not strictly required for all commands)_
You can run the [Mocha](http://mochajs.org/) test suite, you can do:
### Run commands
```sh
$ gulp test
```
Take a look at the full command documentation at [https://docs.resin.io/tools/cli/](https://docs.resin.io/tools/cli/#table-of-contents
), or by running `resin help`.
## Development mode
### Bash completions
The following command will watch for any changes and will run a linter and the whole test suite:
Optionally you can enable tab completions for the bash shell, enabling the shell to provide additional context and automatically complete arguments to`resin`. To enable bash completions, copy the `resin-completion.bash` file to the default bash completions directory (usually `/etc/bash_completion.d/`) or append it to the end of `~/.bash_completion`.
```sh
$ gulp watch
```
FAQ
---
If you set `DEBUG` environment variable, errors will print with a stack trace:
### Where is my configuration file?
```sh
$ DEBUG=true resin ...
```
The per-user configuration file lives in `$HOME/.resinrc.yml` or `%UserProfile%\_resinrc.yml`, in Unix based operating systems and Windows respectively.
## Documentation
The Resin CLI also attempts to read a `resinrc.yml` file in the current directory, which takes precedence over the per-user configuration file.
You can renegerate the documentation with:
### How do I point the Resin CLI to staging?
```sh
$ npm run-script doc
```
The easiest way is to set the `RESINRC_RESIN_URL=resinstaging.io` environment variable.
## Manual pages
Alternatively, you can edit your configuration file and set `resinUrl: resinstaging.io` to persist this setting.
UNIX manual pages reside in `man/`
### How do I make the Resin CLI persist data in another directory?
You can regenerate UNIX `roff` manual pages from markdown with:
The Resin CLI persists your session token, as well as cached images in `$HOME/.resin` or `%UserProfile%\_resin`.
```sh
$ gulp man
```
Pointing the Resin CLI to persist data in another location is necessary in certain environments, like a server, where there is no home directory, or a device running resinOS, which erases all data after a restart.
If you add a new `man` page, remember to add the generated filename to the `man` array in `package.json`.
You can accomplish this by setting `RESINRC_DATA_DIRECTORY=/opt/resin` or adding `dataDirectory: /opt/resin` to your configuration file, replacing `/opt/resin` with your desired directory.
## Caveats
Support
-------
- Some interactive widgets don't work on [Cygwin](https://cygwin.com/). If you're running Windows, it's preferrable that you use `cmd.exe`, as `Cygwin` is [not official supported by Node.js](https://github.com/chjj/blessed/issues/56#issuecomment-42671945).
If you're having any problems, check our [troubleshooting guide](https://github.com/resin-io/resin-cli/blob/master/TROUBLESHOOTING.md) and if your problem is not addressed there, please [raise an issue](https://github.com/resin-io/resin-cli/issues/new) on GitHub and the resin.io team will be happy to help.
You can also get in touch with us in the resin.io [forums](https://forums.resin.io/).
License
-------
The project is licensed under the Apache 2.0 license.

59
TROUBLESHOOTING.md Normal file
View File

@ -0,0 +1,59 @@
Troubleshooting
===============
This document contains common issues related to the Resin CLI, and how to fix them.
### After burning to an sdcard, my device doesn't boot
- The downloaded image is not complete (download was interrupted).
Please clean the cache (`%HOME/.resin/cache` or `C:\Users\<user>\_resin\cache`) and run the command again. In the future, the CLI will check that the image is not complete and clean the cache for you.
### I get a permission error when burning to an sdcard
- The SDCard is locked.
### I get EINVAL errors on Cygwin
The errors look something like this:
```
net.js:156
this._handle.open(options.fd);
^
Error: EINVAL, invalid argument
at new Socket (net.js:156:18)
at process.stdin (node.js:664:19)
at Object.Interface.createInterface (C:\cygwin\home\Juan Cruz Viotti\Projects\resin-cli\node_modules\inquirer\node_modules\readline2\index.js:31:43)
at PromptUI.UI (C:\cygwin\home\Juan Cruz Viotti\Projects\resin-cli\node_modules\inquirer\lib\ui\baseUI.js:23:40)
at new PromptUI (C:\cygwin\home\Juan Cruz Viotti\Projects\resin-cli\node_modules\inquirer\lib\ui\prompt.js:26:8)
at Object.promptModule [as prompt] (C:\cygwin\home\Juan Cruz Viotti\Projects\resin-cli\node_modules\inquirer\lib\inquirer.js:27:14)
```
- Some interactive widgets don't work on `Cygwin`. If you're running Windows, it's preferrable that you use `cmd.exe`, as `Cygwin` is [not official supported by Node.js](https://github.com/chjj/blessed/issues/56#issuecomment-42671945).
### I get `Invalid MBR boot signature` when configuring a device
This error, accompanied with something like: `Expected 0xAA55, but saw 0x29FE` usually indicates a corrupted device operating system image in the cache, due to bad a internet connection during the download process.
Try clearing the cache with the following command and trying again:
```sh
$ rm -rf $HOME/.resin/cache
```
Or in Windows:
```sh
> del /s /q %UserProfile%\_resin\cache
```
### I get `EACCES: permission denied` when logging in
The Resin CLI stores the session token in `$HOME/.resin` or `C:\Users\<user>\_resin` in UNIX based operating systems and Windows respectively. This error usually indicates that the user doesn't have permissions over that directory, which can happen if you ran the Resin CLI as `root`, and thus the directory got owned by him.
Try resetting the ownership by running:
```sh
$ sudo chown -R <user> $HOME/.resin
```

34
appveyor.yml Normal file
View File

@ -0,0 +1,34 @@
# appveyor file
# http://www.appveyor.com/docs/appveyor-yml
init:
- git config --global core.autocrlf input
cache:
- C:\Users\appveyor\.node-gyp
- '%AppData%\npm-cache'
matrix:
fast_finish: true
# what combinations to test
environment:
matrix:
- nodejs_version: 6
install:
- ps: Install-Product node $env:nodejs_version x64
- npm install -g npm@4
- set PATH=%APPDATA%\npm;%PATH%
- npm install
build: off
test_script:
- node --version
- npm --version
- cmd: npm test
deploy_script:
- IF "%APPVEYOR_REPO_TAG%" == "true" (npm run release)
- IF NOT "%APPVEYOR_REPO_TAG%" == "true" (echo 'Not tagged, skipping deploy')

36
automation/build-bin.ts Executable file
View File

@ -0,0 +1,36 @@
import * as path from 'path';
import * as fs from 'fs-extra';
import * as filehound from 'filehound';
import { exec as execPkg } from 'pkg';
const ROOT = path.join(__dirname, '..');
console.log('Building package...\n');
execPkg(['--target', 'host', '--output', 'build-bin/resin', 'package.json'])
.then(() =>
fs.copy(
path.join(ROOT, 'node_modules', 'opn', 'xdg-open'),
path.join(ROOT, 'build-bin', 'xdg-open'),
),
)
.then(() => {
return filehound
.create()
.paths(path.join(ROOT, 'node_modules'))
.ext(['node', 'dll'])
.find();
})
.then(nativeExtensions => {
console.log(`\nCopying to build-bin:\n${nativeExtensions.join('\n')}`);
return nativeExtensions.map(extPath => {
return fs.copy(
extPath,
extPath.replace(
path.join(ROOT, 'node_modules'),
path.join(ROOT, 'build-bin'),
),
);
});
});

14
automation/capitanodoc/doc-types.d.ts vendored Normal file
View File

@ -0,0 +1,14 @@
import { CommandDefinition } from 'capitano';
export interface Document {
title: string;
introduction: string;
categories: Category[];
}
export interface Category {
title: string;
commands: CommandDefinition[];
}
export { CommandDefinition as Command };

View File

@ -0,0 +1,34 @@
import capitanodoc = require('../../capitanodoc');
import * as _ from 'lodash';
import * as path from 'path';
import * as markdown from './markdown';
import { Document, Category } from './doc-types';
const result = <Document>{};
result.title = capitanodoc.title;
result.introduction = capitanodoc.introduction;
result.categories = [];
for (let commandCategory of capitanodoc.categories) {
const category = <Category>{};
category.title = commandCategory.title;
category.commands = [];
for (let file of commandCategory.files) {
// tslint:disable-next-line:no-var-requires
const actions: any = require(path.join(process.cwd(), file));
if (actions.signature) {
category.commands.push(_.omit(actions, 'action'));
} else {
for (let actionName of Object.keys(actions)) {
const actionCommand = actions[actionName];
category.commands.push(_.omit(actionCommand, 'action'));
}
}
}
result.categories.push(category);
}
console.log(markdown.render(result));

View File

@ -0,0 +1,76 @@
import * as _ from 'lodash';
import * as ent from 'ent';
import * as utils from './utils';
import { Document, Category, Command } from './doc-types';
export function renderCommand(command: Command) {
let result = `## ${ent.encode(command.signature)}\n\n${command.help}\n`;
if (!_.isEmpty(command.options)) {
result += '\n### Options';
for (let option of command.options!) {
result += `\n\n#### ${utils.parseSignature(option)}\n\n${
option.description
}`;
}
result += '\n';
}
return result;
}
export function renderCategory(category: Category) {
let result = `# ${category.title}\n`;
for (let command of category.commands) {
result += `\n${renderCommand(command)}`;
}
return result;
}
function getAnchor(command: Command) {
return (
'#' +
command.signature
.replace(/\s/g, '-')
.replace(/</g, '-')
.replace(/>/g, '-')
.replace(/\[/g, '-')
.replace(/\]/g, '-')
.replace(/-+/g, '-')
.replace(/\.\.\./g, '')
.replace(/\|/g, '')
.toLowerCase()
);
}
export function renderToc(categories: Category[]) {
let result = `# Table of contents\n`;
for (let category of categories) {
result += `\n- ${category.title}\n\n`;
for (let command of category.commands) {
result += `\t- [${ent.encode(command.signature)}](${getAnchor(
command,
)})\n`;
}
}
return result;
}
export function render(doc: Document) {
let result = `# ${doc.title}\n\n${doc.introduction}\n\n${renderToc(
doc.categories,
)}`;
for (let category of doc.categories) {
result += `\n${renderCategory(category)}`;
}
return result;
}

View File

@ -0,0 +1,33 @@
import { OptionDefinition } from 'capitano';
import * as _ from 'lodash';
import * as ent from 'ent';
export function getOptionPrefix(signature: string) {
if (signature.length > 1) {
return '--';
} else {
return '-';
}
}
export function getOptionSignature(signature: string) {
return `${getOptionPrefix(signature)}${signature}`;
}
export function parseSignature(option: OptionDefinition) {
let result = getOptionSignature(option.signature);
if (_.isArray(option.alias)) {
for (let alias of option.alias) {
result += `, ${getOptionSignature(alias)}`;
}
} else if (_.isString(option.alias)) {
result += `, ${getOptionSignature(option.alias)}`;
}
if (option.parameter) {
result += ` <${option.parameter}>`;
}
return ent.encode(result);
}

38
automation/custom-types.d.ts vendored Normal file
View File

@ -0,0 +1,38 @@
declare module 'pkg' {
export function exec(args: string[]): Promise<void>;
}
declare module 'filehound' {
export function create(): FileHound;
export interface FileHound {
paths(paths: string[]): FileHound;
paths(...paths: string[]): FileHound;
ext(extensions: string[]): FileHound;
ext(...extensions: string[]): FileHound;
find(): Promise<string[]>;
}
}
declare module 'publish-release' {
interface PublishOptions {
token: string;
owner: string;
repo: string;
tag: string;
name: string;
reuseRelease?: boolean;
assets: string[];
}
interface Release {
html_url: string;
}
let publishRelease: (
args: PublishOptions,
callback: (e: Error, release: Release) => void,
) => void;
export = publishRelease;
}

67
automation/deploy-bin.ts Normal file
View File

@ -0,0 +1,67 @@
import * as Promise from 'bluebird';
import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs-extra';
import * as mkdirp from 'mkdirp';
import * as publishRelease from 'publish-release';
import * as archiver from 'archiver';
import * as packageJSON from '../package.json';
const publishReleaseAsync = Promise.promisify(publishRelease);
const mkdirpAsync = Promise.promisify<string | null, string>(mkdirp);
const { GITHUB_TOKEN } = process.env;
const ROOT = path.join(__dirname, '..');
const version = 'v' + packageJSON.version;
const outputFile = path.join(
ROOT,
'build-zip',
`resin-cli-${version}-${os.platform()}-${os.arch()}.zip`,
);
mkdirpAsync(path.dirname(outputFile))
.then(
() =>
new Promise((resolve, reject) => {
console.log('Zipping build...');
let archive = archiver('zip', {
zlib: { level: 7 },
});
archive.directory(path.join(ROOT, 'build-bin'), 'resin-cli');
let outputStream = fs.createWriteStream(outputFile);
outputStream.on('close', resolve);
outputStream.on('error', reject);
archive.on('error', reject);
archive.on('warning', console.warn);
archive.pipe(outputStream);
archive.finalize();
}),
)
.then(() => {
console.log('Build zipped');
console.log('Publishing build...');
return publishReleaseAsync({
token: <string>GITHUB_TOKEN,
owner: 'resin-io',
repo: 'resin-cli',
tag: version,
name: `Resin-CLI ${version}`,
reuseRelease: true,
assets: [outputFile],
});
})
.then(release => {
console.log(`Release ${version} successful: ${release.html_url}`);
})
.catch(err => {
console.error('Release failed');
console.error(err);
process.exit(1);
});

16
automation/tsconfig.json Normal file
View File

@ -0,0 +1,16 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es2015",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveConstEnums": true,
"removeComments": true,
"sourceMap": true
},
"include": [
"./**/*.ts",
"../typings/*.d.ts"
]
}

View File

@ -1,2 +1,7 @@
#!/usr/bin/env node
// We boost the threadpool size as ext2fs can deadlock with some
// operations otherwise, if the pool runs out.
process.env.UV_THREADPOOL_SIZE = '64';
require('../build/app');

View File

@ -1,16 +0,0 @@
#!/usr/bin/env node
var os = require('../build/actions/os');
var errors = require('../build/errors');
// TODO: Do some error handling when image or device are incorrect
os.install.action({
image: process.argv[2],
device: process.argv[3]
}, {
yes: true,
fromScript: true
}, function(error) {
return errors.handle(error);
});

View File

@ -1,162 +0,0 @@
(function() {
var _, async, commandOptions, path, resin, vcs, visuals;
path = require('path');
_ = require('lodash-contrib');
async = require('async');
resin = require('resin-sdk');
visuals = require('resin-cli-visuals');
commandOptions = require('./command-options');
vcs = require('resin-vcs');
exports.create = {
signature: 'app create <name>',
description: 'create an application',
help: 'Use this command to create a new resin.io application.\n\nYou can specify the application type with the `--type` option.\nOtherwise, an interactive dropdown will be shown for you to select from.\n\nYou can see a list of supported device types with\n\n $ resin devices supported\n\nExamples:\n\n $ resin app create MyApp\n $ resin app create MyApp --type raspberry-pi',
options: [
{
signature: 'type',
parameter: 'type',
description: 'application type',
alias: 't'
}
],
permission: 'user',
action: function(params, options, done) {
return async.waterfall([
function(callback) {
if (options.type != null) {
return callback(null, options.type);
}
return resin.models.device.getSupportedDeviceTypes(function(error, deviceTypes) {
if (error != null) {
return callback(error);
}
return visuals.widgets.select('Select a type', deviceTypes, callback);
});
}, function(type, callback) {
return resin.models.application.create(params.name, type, callback);
}
], done);
}
};
exports.list = {
signature: 'apps',
description: 'list all applications',
help: 'Use this command to list all your applications.\n\nNotice this command only shows the most important bits of information for each app.\nIf you want detailed information, use resin app <name> instead.\n\nExamples:\n\n $ resin apps',
permission: 'user',
action: function(params, options, done) {
return resin.models.application.getAll(function(error, applications) {
if (error != null) {
return done(error);
}
console.log(visuals.widgets.table.horizontal(applications, ['id', 'app_name', 'device_type', 'online_devices', 'devices_length']));
return done();
});
}
};
exports.info = {
signature: 'app <name>',
description: 'list a single application',
help: 'Use this command to show detailed information for a single application.\n\nExamples:\n\n $ resin app MyApp',
permission: 'user',
action: function(params, options, done) {
return resin.models.application.get(params.name, function(error, application) {
if (error != null) {
return done(error);
}
console.log(visuals.widgets.table.vertical(application, ['id', 'app_name', 'device_type', 'git_repository', 'commit']));
return done();
});
}
};
exports.restart = {
signature: 'app restart <name>',
description: 'restart an application',
help: 'Use this command to restart all devices that belongs to a certain application.\n\nExamples:\n\n $ resin app restart MyApp',
permission: 'user',
action: function(params, options, done) {
return resin.models.application.restart(params.name, done);
}
};
exports.remove = {
signature: 'app rm <name>',
description: 'remove an application',
help: 'Use this command to remove a resin.io application.\n\nNotice this command asks for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nExamples:\n\n $ resin app rm MyApp\n $ resin app rm MyApp --yes',
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
return visuals.patterns.remove('application', options.yes, function(callback) {
return resin.models.application.remove(params.name, callback);
}, done);
}
};
exports.associate = {
signature: 'app associate <name>',
description: 'associate a resin project',
help: 'Use this command to associate a project directory with a resin application.\n\nThis command adds a \'resin\' git remote to the directory and runs git init if necessary.\n\nExamples:\n\n $ resin app associate MyApp\n $ resin app associate MyApp --project my/app/directory',
permission: 'user',
action: function(params, options, done) {
var currentDirectory;
currentDirectory = process.cwd();
return async.waterfall([
function(callback) {
return vcs.initialize(currentDirectory, callback);
}, function(callback) {
return resin.models.application.get(params.name, callback);
}, function(application, callback) {
return vcs.addRemote(currentDirectory, application.git_repository, callback);
}
], function(error, remoteUrl) {
if (error != null) {
return done(error);
}
console.info("git repository added: " + remoteUrl);
return done(null, remoteUrl);
});
}
};
exports.init = {
signature: 'init',
description: 'init an application',
help: 'Use this command to initialise a directory as a resin application.\n\nThis command performs the following steps:\n - Create a resin.io application.\n - Initialize the current directory as a git repository.\n - Add the corresponding git remote to the application.\n\nExamples:\n\n $ resin init\n $ resin init --project my/app/directory',
permission: 'user',
action: function(params, options, done) {
var currentDirectory;
currentDirectory = process.cwd();
return async.waterfall([
function(callback) {
var currentDirectoryBasename;
currentDirectoryBasename = path.basename(currentDirectory);
return visuals.widgets.ask('What is the name of your application?', currentDirectoryBasename, callback);
}, function(applicationName, callback) {
return exports.create.action({
name: applicationName
}, options, function(error) {
if (error != null) {
return callback(error);
}
return callback(null, applicationName);
});
}, function(applicationName, callback) {
return exports.associate.action({
name: applicationName
}, options, callback);
}
], done);
}
};
}).call(this);

View File

@ -1,140 +0,0 @@
(function() {
var TOKEN_URL, _, async, open, resin, settings, url, visuals;
open = require('open');
_ = require('lodash-contrib');
url = require('url');
async = require('async');
resin = require('resin-sdk');
settings = require('resin-settings-client');
visuals = require('resin-cli-visuals');
exports.whoami = {
signature: 'whoami',
description: 'whoami',
help: 'Use this command to get the logged in user name.\n\nExamples:\n\n $ resin whoami',
permission: 'user',
action: function(params, options, done) {
return resin.auth.whoami(function(error, username) {
if (error != null) {
return done(error);
}
console.log(username);
return done();
});
}
};
TOKEN_URL = url.resolve(settings.get('remoteUrl'), '/preferences');
exports.login = {
signature: 'login [token]',
description: 'login to resin.io',
help: "Use this command to login to your resin.io account.\n\nTo login, you need your token, which is accesible from the preferences page:\n\n " + TOKEN_URL + "\n\nExamples:\n\n $ resin login\n $ resin login \"eyJ0eXAiOiJKV1Qi...\"",
action: function(params, options, done) {
console.info("To login to the Resin CLI, you need your unique token, which is accesible from\nthe preferences page at " + TOKEN_URL + "\n\nAttempting to open a browser at that location...");
return async.waterfall([
function(callback) {
return open(TOKEN_URL, function(error) {
if (error != null) {
console.error("Unable to open a web browser in the current environment.\nPlease visit " + TOKEN_URL + " manually.");
}
return callback();
});
}, function(callback) {
if (params.token != null) {
return callback(null, params.token);
}
return visuals.widgets.ask('What\'s your token? (visible in the preferences page)', null, callback);
}, function(token, callback) {
return resin.auth.loginWithToken(token, done);
}
], done);
}
};
exports.logout = {
signature: 'logout',
description: 'logout from resin.io',
help: 'Use this command to logout from your resin.io account.o\n\nExamples:\n\n $ resin logout',
permission: 'user',
action: function(params, options, done) {
return resin.auth.logout(done);
}
};
exports.signup = {
signature: 'signup',
description: 'signup to resin.io',
help: 'Use this command to signup for a resin.io account.\n\nIf signup is successful, you\'ll be logged in to your new user automatically.\n\nExamples:\n\n $ resin signup\n Email: me@mycompany.com\n Username: johndoe\n Password: ***********\n\n $ resin signup --email me@mycompany.com --username johndoe --password ***********\n\n $ resin whoami\n johndoe',
options: [
{
signature: 'email',
parameter: 'email',
description: 'user email',
alias: 'e'
}, {
signature: 'username',
parameter: 'username',
description: 'user name',
alias: 'u'
}, {
signature: 'password',
parameter: 'user password',
description: 'user password',
alias: 'p'
}
],
action: function(params, options, done) {
var hasOptionCredentials;
hasOptionCredentials = !_.isEmpty(options);
if (hasOptionCredentials) {
if (options.email == null) {
return done(new Error('Missing email'));
}
if (options.username == null) {
return done(new Error('Missing username'));
}
if (options.password == null) {
return done(new Error('Missing password'));
}
}
return async.waterfall([
function(callback) {
if (hasOptionCredentials) {
return callback(null, options);
}
return visuals.widgets.register(callback);
}, function(credentials, callback) {
return resin.auth.register(credentials, function(error, token) {
return callback(error, credentials);
});
}, function(credentials, callback) {
return resin.auth.login(credentials, callback);
}
], done);
}
};
exports.whoami = {
signature: 'whoami',
description: 'get current username',
help: 'Use this command to find out the current logged in username.\n\nExamples:\n\n $ resin whoami',
permission: 'user',
action: function(params, options, done) {
return resin.auth.whoami(function(error, username) {
if (username == null) {
return done(new Error('Username not found'));
}
return console.log(username);
});
}
};
}).call(this);

View File

@ -1,45 +0,0 @@
(function() {
var _;
_ = require('lodash');
exports.yes = {
signature: 'yes',
description: 'confirm non interactively',
boolean: true,
alias: 'y'
};
exports.optionalApplication = {
signature: 'application',
parameter: 'application',
description: 'application name',
alias: ['a', 'app']
};
exports.application = _.defaults({
required: 'You have to specify an application'
}, exports.optionalApplication);
exports.network = {
signature: 'network',
parameter: 'network',
description: 'network type',
alias: 'n'
};
exports.wifiSsid = {
signature: 'ssid',
parameter: 'ssid',
description: 'wifi ssid, if network is wifi',
alias: 's'
};
exports.wifiKey = {
signature: 'key',
parameter: 'key',
description: 'wifi key, if network is wifi',
alias: 'k'
};
}).call(this);

View File

@ -1,150 +0,0 @@
(function() {
var _, async, commandOptions, osAction, path, resin, vcs, visuals;
_ = require('lodash-contrib');
path = require('path');
async = require('async');
resin = require('resin-sdk');
visuals = require('resin-cli-visuals');
vcs = require('resin-vcs');
commandOptions = require('./command-options');
osAction = require('./os');
exports.list = {
signature: 'devices',
description: 'list all devices',
help: 'Use this command to list all devices that belong to a certain application.\n\nExamples:\n\n $ resin devices --application MyApp',
options: [commandOptions.application],
permission: 'user',
action: function(params, options, done) {
return resin.models.device.getAllByApplication(options.application, function(error, devices) {
if (error != null) {
return done(error);
}
console.log(visuals.widgets.table.horizontal(devices, ['id', 'name', 'device_type', 'is_online', 'application_name', 'status', 'last_seen']));
return done();
});
}
};
exports.info = {
signature: 'device <name>',
description: 'list a single device',
help: 'Use this command to show information about a single device.\n\nExamples:\n\n $ resin device MyDevice',
permission: 'user',
action: function(params, options, done) {
return resin.models.device.get(params.name, function(error, device) {
if (error != null) {
return done(error);
}
console.log(visuals.widgets.table.vertical(device, ['id', 'name', 'device_type', 'is_online', 'ip_address', 'application_name', 'status', 'last_seen', 'uuid', 'commit', 'supervisor_version', 'is_web_accessible', 'note']));
return done();
});
}
};
exports.remove = {
signature: 'device rm <name>',
description: 'remove a device',
help: 'Use this command to remove a device from resin.io.\n\nNotice this command asks for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nExamples:\n\n $ resin device rm MyDevice\n $ resin device rm MyDevice --yes',
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
return visuals.patterns.remove('device', options.yes, function(callback) {
return resin.models.device.remove(params.name, callback);
}, done);
}
};
exports.identify = {
signature: 'device identify <uuid>',
description: 'identify a device with a UUID',
help: 'Use this command to identify a device.\n\nIn the Raspberry Pi, the ACT led is blinked several times.\n\nExamples:\n\n $ resin device identify 23c73a12e3527df55c60b9ce647640c1b7da1b32d71e6a39849ac0f00db828',
permission: 'user',
action: function(params, options, done) {
return resin.models.device.identify(params.uuid, done);
}
};
exports.rename = {
signature: 'device rename <name> [newName]',
description: 'rename a resin device',
help: 'Use this command to rename a device.\n\nIf you omit the name, you\'ll get asked for it interactively.\n\nExamples:\n\n $ resin device rename MyDevice MyPi\n $ resin device rename MyDevice',
permission: 'user',
action: function(params, options, done) {
return async.waterfall([
function(callback) {
if (!_.isEmpty(params.newName)) {
return callback(null, params.newName);
}
return visuals.widgets.ask('How do you want to name this device?', null, callback);
}, function(newName, callback) {
return resin.models.device.rename(params.name, newName, callback);
}
], done);
}
};
exports.supported = {
signature: 'devices supported',
description: 'list all supported devices',
help: 'Use this command to get the list of all supported devices\n\nExamples:\n\n $ resin devices supported',
permission: 'user',
action: function(params, options, done) {
return resin.models.device.getSupportedDeviceTypes(function(error, devices) {
if (error != null) {
return done(error);
}
_.each(devices, _.unary(console.log));
return done();
});
}
};
exports.init = {
signature: 'device init [device]',
description: 'initialise a device with resin os',
help: 'Use this command to download the OS image of a certain application and write it to an SD Card.\n\nNote that this command requires admin privileges.\n\nIf `device` is omitted, you will be prompted to select a device interactively.\n\nNotice this command asks for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nYou can quiet the progress bar by passing the `--quiet` boolean option.\n\nYou may have to unmount the device before attempting this operation.\n\nYou need to configure the network type and other settings:\n\nEthernet:\n You can setup the device OS to use ethernet by setting the `--network` option to "ethernet".\n\nWifi:\n You can setup the device OS to use wifi by setting the `--network` option to "wifi".\n If you set "network" to "wifi", you will need to specify the `--ssid` and `--key` option as well.\n\nYou can omit network related options to be asked about them interactively.\n\nExamples:\n\n $ resin device init\n $ resin device init --application 91\n $ resin device init --application 91 --network ethernet\n $ resin device init /dev/disk2 --application 91 --network wifi --ssid MyNetwork --key secret',
options: [commandOptions.optionalApplication, commandOptions.network, commandOptions.wifiSsid, commandOptions.wifiKey],
permission: 'user',
action: function(params, options, done) {
params.id = options.application;
return async.waterfall([
function(callback) {
if (options.application != null) {
return callback(null, options.application);
}
return vcs.getApplicationId(process.cwd(), callback);
}, function(applicationId, callback) {
params.id = applicationId;
if (params.device != null) {
return callback(null, params.device);
}
return visuals.patterns.selectDrive(callback);
}, function(device, callback) {
var message;
params.device = device;
message = "This will completely erase " + params.device + ". Are you sure you want to continue?";
return visuals.patterns.confirm(options.yes, message, callback);
}, function(confirmed, callback) {
if (!confirmed) {
return done();
}
options.yes = confirmed;
return osAction.download.action(params, options, callback);
}, function(outputFile, callback) {
params.image = outputFile;
return osAction.install.action(params, options, callback);
}
], done);
}
};
}).call(this);

View File

@ -1,33 +0,0 @@
(function() {
var _, async, drivelist, visuals;
_ = require('lodash');
async = require('async');
visuals = require('resin-cli-visuals');
drivelist = require('drivelist');
exports.list = {
signature: 'drives',
description: 'list available drives',
help: 'Use this command to list all drives that are connected to your machine.\n\nExamples:\n\n $ resin drives',
permission: 'user',
action: function(params, options, done) {
return drivelist.list(function(error, drives) {
if (error != null) {
return done(error);
}
return async.reject(drives, drivelist.isSystem, function(removableDrives) {
if (_.isEmpty(removableDrives)) {
return done(new Error('No removable devices available'));
}
console.log(visuals.widgets.table.horizontal(removableDrives, ['device', 'description', 'size']));
return done();
});
});
}
};
}).call(this);

View File

@ -1,81 +0,0 @@
(function() {
var _, commandOptions, resin, visuals;
_ = require('lodash-contrib');
resin = require('resin-sdk');
visuals = require('resin-cli-visuals');
commandOptions = require('./command-options');
exports.list = {
signature: 'envs',
description: 'list all environment variables',
help: 'Use this command to list all environment variables for a particular application.\nNotice we will support per-device environment variables soon.\n\nThis command lists all custom environment variables set on the devices running\nthe application. If you want to see all environment variables, including private\nones used by resin, use the verbose option.\n\nExample:\n\n $ resin envs --application 91\n $ resin envs --application 91 --verbose',
options: [
commandOptions.application, {
signature: 'verbose',
description: 'show private environment variables',
boolean: true,
alias: 'v'
}
],
permission: 'user',
action: function(params, options, done) {
return resin.models.environmentVariables.getAllByApplication(options.application, function(error, environmentVariables) {
if (error != null) {
return done(error);
}
if (!options.verbose) {
environmentVariables = _.reject(environmentVariables, resin.models.environmentVariables.isSystemVariable);
}
console.log(visuals.widgets.table.horizontal(environmentVariables, ['id', 'name', 'value']));
return done();
});
}
};
exports.remove = {
signature: 'env rm <id>',
description: 'remove an environment variable',
help: 'Use this command to remove an environment variable from an application.\n\nDon\'t remove resin specific variables, as things might not work as expected.\n\nNotice this command asks for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nExamples:\n\n $ resin env rm 215\n $ resin env rm 215 --yes',
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
return visuals.patterns.remove('environment variable', options.yes, function(callback) {
return resin.models.environmentVariables.remove(params.id, callback);
}, done);
}
};
exports.add = {
signature: 'env add <key> [value]',
description: 'add an environment variable',
help: 'Use this command to add an enviroment variable to an application.\n\nYou need to pass the `--application` option.\n\nIf value is omitted, the tool will attempt to use the variable\'s value\nas defined in your host machine.\n\nIf the value is grabbed from the environment, a warning message will be printed.\nUse `--quiet` to remove it.\n\nExamples:\n\n $ resin env add EDITOR vim -a 91\n $ resin env add TERM -a 91',
options: [commandOptions.application],
permission: 'user',
action: function(params, options, done) {
if (params.value == null) {
params.value = process.env[params.key];
if (params.value == null) {
return done(new Error("Environment value not found for key: " + params.key));
} else {
console.info("Warning: using " + params.key + "=" + params.value + " from host environment");
}
}
return resin.models.environmentVariables.create(options.application, params.key, params.value, done);
}
};
exports.rename = {
signature: 'env rename <id> <value>',
description: 'rename an environment variable',
help: 'Use this command to rename an enviroment variable from an application.\n\nExamples:\n\n $ resin env rename 376 emacs',
permission: 'user',
action: function(params, options, done) {
return resin.models.environmentVariables.update(params.id, params.value, done);
}
};
}).call(this);

View File

@ -1,78 +0,0 @@
(function() {
var _, async, examplesData, fs, path, resin, vcs, visuals;
async = require('async');
fs = require('fs');
path = require('path');
_ = require('lodash');
resin = require('resin-sdk');
visuals = require('resin-cli-visuals');
vcs = require('resin-vcs');
examplesData = require('../data/examples.json');
exports.list = {
signature: 'examples',
description: 'list all example applications',
help: 'Use this command to list available example applications from resin.io\n\nExample:\n\n $ resin examples',
permission: 'user',
action: function() {
examplesData = _.map(examplesData, function(example, index) {
example.id = index + 1;
return example;
});
examplesData = _.map(examplesData, function(example) {
if (example.author == null) {
example.author = 'Unknown';
}
return example;
});
return console.log(visuals.widgets.table.horizontal(examplesData, ['id', 'display_name', 'repository', 'author']));
}
};
exports.info = {
signature: 'example <id>',
description: 'list a single example application',
help: 'Use this command to show information of a single example application\n\nExample:\n\n $ resin example 3',
permission: 'user',
action: function(params, options, done) {
var example, id;
id = params.id - 1;
example = examplesData[id];
if (example == null) {
return done(new Error("Unknown example: " + id));
}
example.id = id;
if (example.author == null) {
example.author = 'Unknown';
}
console.log(visuals.widgets.table.vertical(example, ['id', 'display_name', 'description', 'author', 'repository']));
return done();
}
};
exports.clone = {
signature: 'example clone <id>',
description: 'clone an example application',
help: 'Use this command to clone an example application to the current directory\n\nThis command outputs information about the cloning process.\nUse `--quiet` to remove that output.\n\nExample:\n\n $ resin example clone 3',
permission: 'user',
action: function(params, options, done) {
var currentDirectory, example;
example = examplesData[params.id - 1];
if (example == null) {
return done(new Error("Unknown example: " + id));
}
currentDirectory = process.cwd();
console.info("Cloning " + example.display_name + " to " + currentDirectory);
return vcs.clone(example.repository, currentDirectory, done);
}
};
}).call(this);

View File

@ -1,162 +0,0 @@
(function() {
var PADDING_INITIAL, PADDING_MIDDLE, _, addAlias, addOptionPrefix, buildHelpString, buildOptionSignatureHelp, capitano, command, general, getCommandHelp, getFieldMaxLength, getOptionHelp, getOptionsParsedSignatures, resin;
_ = require('lodash');
_.str = require('underscore.string');
resin = require('resin-sdk');
capitano = require('capitano');
PADDING_INITIAL = ' ';
PADDING_MIDDLE = '\t';
getFieldMaxLength = function(array, field) {
return _.max(_.map(array, function(item) {
return item[field].toString().length;
}));
};
buildHelpString = function(firstColumn, secondColumn) {
var result;
result = "" + PADDING_INITIAL + firstColumn;
result += "" + PADDING_MIDDLE + secondColumn;
return result;
};
addOptionPrefix = function(option) {
if (option.length <= 0) {
return;
}
if (option.length === 1) {
return "-" + option;
} else {
return "--" + option;
}
};
addAlias = function(alias) {
return ", " + (addOptionPrefix(alias));
};
buildOptionSignatureHelp = function(option) {
var alias, i, len, ref, result;
result = addOptionPrefix(option.signature.toString());
if (_.isString(option.alias)) {
result += addAlias(option.alias);
} else if (_.isArray(option.alias)) {
ref = option.alias;
for (i = 0, len = ref.length; i < len; i++) {
alias = ref[i];
result += addAlias(alias);
}
}
if (option.parameter != null) {
result += " <" + option.parameter + ">";
}
return result;
};
getCommandHelp = function(command) {
var commandSignature, maxSignatureLength;
maxSignatureLength = getFieldMaxLength(capitano.state.commands, 'signature');
commandSignature = _.str.rpad(command.signature.toString(), maxSignatureLength, ' ');
return buildHelpString(commandSignature, command.description);
};
getOptionsParsedSignatures = function(optionsHelp) {
var maxLength;
maxLength = _.max(_.map(optionsHelp, function(signature) {
return signature.length;
}));
return _.map(optionsHelp, function(signature) {
return _.str.rpad(signature, maxLength, ' ');
});
};
getOptionHelp = function(option, maxLength) {
var result;
result = PADDING_INITIAL;
result += _.str.rpad(option.signature, maxLength, ' ');
result += PADDING_MIDDLE;
result += option.description;
return result;
};
general = function() {
var command, i, j, len, len1, option, optionSignatureMaxLength, options, ref;
console.log('Usage: resin [COMMAND] [OPTIONS]\n');
console.log('Commands:\n');
ref = capitano.state.commands;
for (i = 0, len = ref.length; i < len; i++) {
command = ref[i];
if (command.isWildcard()) {
continue;
}
console.log(getCommandHelp(command));
}
console.log('\nGlobal Options:\n');
options = _.map(capitano.state.globalOptions, function(option) {
option.signature = buildOptionSignatureHelp(option);
return option;
});
optionSignatureMaxLength = _.max(_.map(options, function(option) {
return option.signature.length;
}));
for (j = 0, len1 = options.length; j < len1; j++) {
option = options[j];
console.log(getOptionHelp(option, optionSignatureMaxLength));
}
return console.log();
};
command = function(params, options, done) {
return capitano.state.getMatchCommand(params.command, function(error, command) {
var i, len, option, optionSignatureMaxLength;
if (error != null) {
return done(error);
}
if ((command == null) || command.isWildcard()) {
return capitano.defaults.actions.commandNotFound(params.command);
}
console.log("Usage: " + command.signature);
if (command.help != null) {
console.log("\n" + command.help);
} else if (command.description != null) {
console.log("\n" + (_.str.humanize(command.description)));
}
if (!_.isEmpty(command.options)) {
console.log('\nOptions:\n');
options = _.map(command.options, function(option) {
option.signature = buildOptionSignatureHelp(option);
return option;
});
optionSignatureMaxLength = _.max(_.map(options, function(option) {
return option.signature.toString().length;
}));
for (i = 0, len = options.length; i < len; i++) {
option = options[i];
console.log(getOptionHelp(option, optionSignatureMaxLength));
}
console.log();
}
return done();
});
};
exports.help = {
signature: 'help [command...]',
description: 'show help',
help: 'Get detailed help for an specific command.\n\nExamples:\n\n $ resin help apps\n $ resin help os download',
action: function(params, options, done) {
if (params.command != null) {
return command(params, options, done);
} else {
return general(params, options, done);
}
}
};
}).call(this);

View File

@ -1,20 +0,0 @@
(function() {
module.exports = {
app: require('./app'),
info: require('./info'),
auth: require('./auth'),
drive: require('./drive'),
device: require('./device'),
env: require('./environment-variables'),
keys: require('./keys'),
logs: require('./logs'),
notes: require('./notes'),
preferences: require('./preferences'),
os: require('./os'),
help: require('./help'),
examples: require('./examples'),
plugin: require('./plugin'),
update: require('./update')
};
}).call(this);

View File

@ -1,15 +0,0 @@
(function() {
var packageJSON;
packageJSON = require('../../package.json');
exports.version = {
signature: 'version',
description: 'output the version number',
help: 'Display the Resin CLI version.',
action: function() {
return console.log(packageJSON.version);
}
};
}).call(this);

View File

@ -1,92 +0,0 @@
(function() {
var SSH_KEY_WIDTH, _, async, capitano, commandOptions, fs, resin, visuals;
_ = require('lodash');
_.str = require('underscore.string');
async = require('async');
fs = require('fs');
resin = require('resin-sdk');
capitano = require('capitano');
visuals = require('resin-cli-visuals');
commandOptions = require('./command-options');
exports.list = {
signature: 'keys',
description: 'list all ssh keys',
help: 'Use this command to list all your SSH keys.\n\nExamples:\n\n $ resin keys',
permission: 'user',
action: function(params, options, done) {
return resin.models.key.getAll(function(error, keys) {
if (error != null) {
return done(error);
}
console.log(visuals.widgets.table.horizontal(keys, ['id', 'title']));
return done();
});
}
};
SSH_KEY_WIDTH = 43;
exports.info = {
signature: 'key <id>',
description: 'list a single ssh key',
help: 'Use this command to show information about a single SSH key.\n\nExamples:\n\n $ resin key 17',
permission: 'user',
action: function(params, options, done) {
return resin.models.key.get(params.id, function(error, key) {
if (error != null) {
return done(error);
}
key.public_key = '\n' + visuals.helpers.chop(key.public_key, SSH_KEY_WIDTH);
console.log(visuals.widgets.table.vertical(key, ['id', 'title', 'public_key']));
return done();
});
}
};
exports.remove = {
signature: 'key rm <id>',
description: 'remove a ssh key',
help: 'Use this command to remove a SSH key from resin.io.\n\nNotice this command asks for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nExamples:\n\n $ resin key rm 17\n $ resin key rm 17 --yes',
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
return visuals.patterns.remove('key', options.yes, function(callback) {
return resin.models.key.remove(params.id, callback);
}, done);
}
};
exports.add = {
signature: 'key add <name> [path]',
description: 'add a SSH key to resin.io',
help: 'Use this command to associate a new SSH key with your account.\n\nIf `path` is omitted, the command will attempt\nto read the SSH key from stdin.\n\nExamples:\n\n $ resin key add Main ~/.ssh/id_rsa.pub\n $ cat ~/.ssh/id_rsa.pub | resin key add Main',
permission: 'user',
action: function(params, options, done) {
return async.waterfall([
function(callback) {
if (params.path != null) {
return fs.readFile(params.path, {
encoding: 'utf8'
}, callback);
} else {
return capitano.utils.getStdin(function(data) {
return callback(null, data);
});
}
}, function(key, callback) {
return resin.models.key.create(params.name, key, callback);
}
], done);
}
};
}).call(this);

View File

@ -1,48 +0,0 @@
(function() {
var LOGS_HISTORY_COUNT, _, resin;
_ = require('lodash');
resin = require('resin-sdk');
LOGS_HISTORY_COUNT = 200;
exports.logs = {
signature: 'logs <uuid>',
description: 'show device logs',
help: 'Use this command to show logs for a specific device.\n\nBy default, the command prints all log messages and exit.\n\nTo limit the output to the n last lines, use the `--num` option along with a number.\nThis is similar to doing `resin logs <uuid> | tail -n X`.\n\nTo continuously stream output, and see new logs in real time, use the `--tail` option.\n\nNote that for now you need to provide the whole UUID for this command to work correctly,\nand the tool won\'t notice if you\'re using an invalid UUID.\n\nThis is due to some technical limitations that we plan to address soon.\n\nExamples:\n\n $ resin logs 23c73a12e3527df55c60b9ce647640c1b7da1b32d71e6a39849ac0f00db828\n $ resin logs 23c73a12e3527df55c60b9ce647640c1b7da1b32d71e6a39849ac0f00db828 --num 20\n $ resin logs 23c73a12e3527df55c60b9ce647640c1b7da1b32d71e6a39849ac0f00db828 --tail',
options: [
{
signature: 'num',
parameter: 'num',
description: 'number of lines to display',
alias: 'n'
}, {
signature: 'tail',
description: 'continuously stream output',
boolean: true,
alias: 't'
}
],
permission: 'user',
action: function(params, options, done) {
return resin.logs.subscribe(params.uuid, {
history: options.num || LOGS_HISTORY_COUNT,
tail: options.tail
}, function(error, message) {
if (error != null) {
return done(error);
}
if (_.isArray(message)) {
_.each(message, function(line) {
return console.log(line);
});
} else {
console.log(message);
}
return done();
});
}
};
}).call(this);

View File

@ -1,27 +0,0 @@
(function() {
var async, resin;
async = require('async');
resin = require('resin-sdk');
exports.set = {
signature: 'note <|note>',
description: 'set a device note',
help: 'Use this command to set or update a device note.\n\nIf note command isn\'t passed, the tool attempts to read from `stdin`.\n\nTo view the notes, use $ resin device <name>.\n\nExamples:\n\n $ resin note "My useful note" --device MyDevice\n $ cat note.txt | resin note --device MyDevice',
options: [
{
signature: 'device',
parameter: 'device',
description: 'device name',
alias: ['d', 'dev'],
required: 'You have to specify a device'
}
],
permission: 'user',
action: function(params, options, done) {
return resin.models.device.note(options.device, params.note, done);
}
};
}).call(this);

View File

@ -1,152 +0,0 @@
(function() {
var _, async, commandOptions, elevate, mkdirp, npm, os, packageJSON, path, resin, updateActions, visuals;
_ = require('lodash-contrib');
os = require('os');
async = require('async');
path = require('path');
mkdirp = require('mkdirp');
resin = require('resin-sdk');
visuals = require('resin-cli-visuals');
commandOptions = require('./command-options');
npm = require('../npm');
packageJSON = require('../../package.json');
updateActions = require('./update');
elevate = require('../elevate');
exports.download = {
signature: 'os download <id>',
description: 'download device OS',
help: 'Use this command to download the device OS configured to a specific network.\n\nEthernet:\n You can setup the device OS to use ethernet by setting the `--network` option to "ethernet".\n\nWifi:\n You can setup the device OS to use wifi by setting the `--network` option to "wifi".\n If you set "network" to "wifi", you will need to specify the `--ssid` and `--key` option as well.\n\nAlternatively, you can omit all kind of network configuration options to configure interactively.\n\nYou have to specify an output location with the `--output` option.\n\nExamples:\n\n $ resin os download 91 --output ~/MyResinOS.zip\n $ resin os download 91 --network ethernet --output ~/MyResinOS.zip\n $ resin os download 91 --network wifi --ssid MyNetwork --key secreykey123 --output ~/MyResinOS.zip\n $ resin os download 91 --network ethernet --output ~/MyResinOS.zip',
options: [
commandOptions.network, commandOptions.wifiSsid, commandOptions.wifiKey, {
signature: 'output',
parameter: 'output',
description: 'output file',
alias: 'o',
required: 'You need to specify an output file'
}
],
permission: 'user',
action: function(params, options, done) {
var osParams;
osParams = {
network: options.network,
wifiSsid: options.ssid,
wifiKey: options.key,
appId: params.id
};
return async.waterfall([
function(callback) {
if (osParams.network != null) {
return callback();
}
return visuals.patterns.selectNetworkParameters(function(error, parameters) {
if (error != null) {
return callback(error);
}
_.extend(osParams, parameters);
return callback();
});
}, function(callback) {
return mkdirp(path.dirname(options.output), _.unary(callback));
}, function(callback) {
var bar, spinner;
console.info("Destination file: " + options.output + "\n");
bar = new visuals.widgets.Progress('Downloading Device OS');
spinner = new visuals.widgets.Spinner('Downloading Device OS (size unknown)');
return resin.models.os.download(osParams, options.output, function(error) {
spinner.stop();
if (error != null) {
return callback(error);
}
}, function(state) {
if (state != null) {
return bar.update(state);
} else {
return spinner.start();
}
});
}
], function(error) {
if (error != null) {
return done(error);
}
console.info("\nFinished downloading " + options.output);
return done(null, options.output);
});
}
};
exports.install = {
signature: 'os install <image> [device]',
description: 'write an operating system image to a device',
help: 'Use this command to write an operating system image to a device.\n\nNote that this command requires admin privileges.\n\nIf `device` is omitted, you will be prompted to select a device interactively.\n\nNotice this command asks for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nYou can quiet the progress bar by passing the `--quiet` boolean option.\n\nYou may have to unmount the device before attempting this operation.\n\nSee the `drives` command to get a list of all connected devices to your machine and their respective ids.\n\nIn Mac OS X:\n\n $ sudo diskutil unmountDisk /dev/xxx\n\nIn GNU/Linux:\n\n $ sudo umount /dev/xxx\n\nExamples:\n\n $ resin os install rpi.iso /dev/disk2',
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
var bundle;
bundle = require('../devices/raspberry-pi');
return async.waterfall([
function(callback) {
return npm.isUpdated(packageJSON.name, packageJSON.version, callback);
}, function(isUpdated, callback) {
if (isUpdated) {
return callback();
}
console.info('Resin CLI is outdated.\n\nIn order to avoid device compatibility issues, this command\nrequires that you have the Resin CLI updated.\n\nUpdating now...');
return updateActions.update.action(params, options, _.unary(callback));
}, function(callback) {
if (params.device != null) {
return callback(null, params.device);
}
return visuals.patterns.selectDrive(function(error, device) {
if (error != null) {
return callback(error);
}
if (device == null) {
return callback(new Error('No removable devices available'));
}
return callback(null, device);
});
}, function(device, callback) {
var message;
params.device = device;
message = "This will completely erase " + params.device + ". Are you sure you want to continue?";
return visuals.patterns.confirm(options.yes, message, callback);
}, function(confirmed, callback) {
var bar;
if (!confirmed) {
return done();
}
bar = new visuals.widgets.Progress('Writing Device OS');
params.progress = _.bind(bar.update, bar);
return bundle.write(params, callback);
}
], function(error) {
var resinWritePath;
if (error == null) {
return done();
}
if (elevate.shouldElevate(error) && !options.fromScript) {
resinWritePath = "\"" + (path.join(__dirname, '..', '..', 'bin', 'resin-write')) + "\"";
return elevate.run("\"" + process.argv[0] + "\" " + resinWritePath + " \"" + params.image + "\" \"" + params.device + "\"");
} else {
return done(error);
}
});
}
};
}).call(this);

View File

@ -1,83 +0,0 @@
(function() {
var _, commandOptions, plugins, visuals;
_ = require('lodash');
visuals = require('resin-cli-visuals');
commandOptions = require('./command-options');
plugins = require('../plugins');
exports.list = {
signature: 'plugins',
description: 'list all plugins',
help: 'Use this command to list all the installed resin plugins.\n\nExamples:\n\n $ resin plugins',
permission: 'user',
action: function(params, options, done) {
return plugins.list(function(error, resinPlugins) {
if (error != null) {
return done(error);
}
if (_.isEmpty(resinPlugins)) {
console.log('You don\'t have any plugins yet');
return done();
}
console.log(visuals.widgets.table.horizontal(resinPlugins, ['name', 'version', 'description', 'license']));
return done();
});
}
};
exports.install = {
signature: 'plugin install <name>',
description: 'install a plugin',
help: 'Use this command to install a resin plugin\n\nUse `--quiet` to prevent information logging.\n\nExamples:\n\n $ resin plugin install hello',
permission: 'user',
action: function(params, options, done) {
return plugins.install(params.name, function(error) {
if (error != null) {
return done(error);
}
console.info("Plugin installed: " + params.name);
return done();
});
}
};
exports.update = {
signature: 'plugin update <name>',
description: 'update a plugin',
help: 'Use this command to update a resin plugin\n\nUse `--quiet` to prevent information logging.\n\nExamples:\n\n $ resin plugin update hello',
permission: 'user',
action: function(params, options, done) {
return plugins.update(params.name, function(error, version) {
if (error != null) {
return done(error);
}
console.info("Plugin updated: " + params.name + "@" + version);
return done();
});
}
};
exports.remove = {
signature: 'plugin rm <name>',
description: 'remove a plugin',
help: 'Use this command to remove a resin.io plugin.\n\nNotice this command asks for confirmation interactively.\nYou can avoid this by passing the `--yes` boolean option.\n\nExamples:\n\n $ resin plugin rm hello\n $ resin plugin rm hello --yes',
options: [commandOptions.yes],
permission: 'user',
action: function(params, options, done) {
return visuals.patterns.remove('plugin', options.yes, function(callback) {
return plugins.remove(params.name, callback);
}, function(error) {
if (error != null) {
return done(error);
}
console.info("Plugin removed: " + params.name);
return done();
});
}
};
}).call(this);

View File

@ -1,22 +0,0 @@
(function() {
var open, settings, url;
open = require('open');
url = require('url');
settings = require('resin-settings-client');
exports.preferences = {
signature: 'preferences',
description: 'open preferences form',
help: 'Use this command to open the preferences form.\n\nIn the future, we will allow changing all preferences directly from the terminal.\nFor now, we open your default web browser and point it to the web based preferences form.\n\nExamples:\n\n $ resin preferences',
permission: 'user',
action: function() {
var absUrl;
absUrl = url.resolve(settings.get('remoteUrl'), '/preferences');
return open(absUrl);
}
};
}).call(this);

View File

@ -1,51 +0,0 @@
(function() {
var _, child_process, npm, packageJSON, president;
_ = require('lodash');
child_process = require('child_process');
president = require('president');
npm = require('../npm');
packageJSON = require('../../package.json');
exports.update = {
signature: 'update',
description: 'update the resin cli',
help: 'Use this command to update the Resin CLI\n\nThis command outputs information about the update process.\nUse `--quiet` to remove that output.\n\nThe Resin CLI checks for updates once per day.\n\nMajor updates require a manual update with this update command,\nwhile minor updates are applied automatically.\n\nExamples:\n\n $ resin update',
action: function(params, options, done) {
return npm.isUpdated(packageJSON.name, packageJSON.version, function(error, isUpdated) {
var command, onUpdate;
if (error != null) {
return done(error);
}
if (isUpdated) {
return done(new Error('You\'re already running the latest version.'));
}
onUpdate = function(error, stdout, stderr) {
if (error != null) {
return done(error);
}
if (!_.isEmpty(stderr)) {
return done(new Error(stderr));
}
console.info("Upgraded " + packageJSON.name + ".");
return done();
};
command = "npm install --global " + packageJSON.name;
return child_process.exec(command, function(error, stdout, stderr) {
if (error != null) {
return onUpdate(null, stdout, stderr);
}
if (_.any([error.code === 3, error.code === 'EPERM', error.code === 'ACCES'])) {
return president.execute(command, onUpdate);
}
return done(error);
});
});
}
};
}).call(this);

View File

@ -1,168 +0,0 @@
(function() {
var _, actions, async, capitano, changeProjectDirectory, errors, plugins, resin, update;
_ = require('lodash');
async = require('async');
capitano = require('capitano');
resin = require('resin-sdk');
actions = require('./actions');
errors = require('./errors');
plugins = require('./plugins');
update = require('./update');
capitano.permission('user', function(done) {
return resin.auth.isLoggedIn(function(isLoggedIn) {
if (!isLoggedIn) {
return done(new Error('You have to log in'));
}
return done();
});
});
capitano.command({
signature: '*',
action: function() {
return capitano.execute({
command: 'help'
});
}
});
capitano.globalOption({
signature: 'quiet',
description: 'quiet (no output)',
boolean: true,
alias: 'q'
});
capitano.globalOption({
signature: 'project',
parameter: 'path',
description: 'project path',
alias: 'j'
});
capitano.globalOption({
signature: 'no-color',
description: 'disable colour highlighting',
boolean: true
});
capitano.command(actions.info.version);
capitano.command(actions.help.help);
capitano.command(actions.auth.login);
capitano.command(actions.auth.logout);
capitano.command(actions.auth.signup);
capitano.command(actions.auth.whoami);
capitano.command(actions.app.create);
capitano.command(actions.app.list);
capitano.command(actions.app.info);
capitano.command(actions.app.remove);
capitano.command(actions.app.restart);
capitano.command(actions.app.associate);
capitano.command(actions.app.init);
capitano.command(actions.device.list);
capitano.command(actions.device.supported);
capitano.command(actions.device.rename);
capitano.command(actions.device.init);
capitano.command(actions.device.info);
capitano.command(actions.device.remove);
capitano.command(actions.device.identify);
capitano.command(actions.drive.list);
capitano.command(actions.notes.set);
capitano.command(actions.preferences.preferences);
capitano.command(actions.keys.list);
capitano.command(actions.keys.add);
capitano.command(actions.keys.info);
capitano.command(actions.keys.remove);
capitano.command(actions.env.list);
capitano.command(actions.env.add);
capitano.command(actions.env.rename);
capitano.command(actions.env.remove);
capitano.command(actions.logs.logs);
capitano.command(actions.os.download);
capitano.command(actions.os.install);
capitano.command(actions.examples.list);
capitano.command(actions.examples.clone);
capitano.command(actions.examples.info);
capitano.command(actions.plugin.list);
capitano.command(actions.plugin.install);
capitano.command(actions.plugin.update);
capitano.command(actions.plugin.remove);
capitano.command(actions.update.update);
changeProjectDirectory = function(directory) {
try {
return process.chdir(directory);
} catch (_error) {
return errors.handle(new Error("Invalid project: " + directory));
}
};
async.waterfall([
function(callback) {
return update.check(callback);
}, function(callback) {
return plugins.register('resin-plugin-', callback);
}, function(callback) {
var cli;
cli = capitano.parse(process.argv);
if (cli.global.quiet) {
console.info = _.noop;
}
if (cli.global.project != null) {
changeProjectDirectory(cli.global.project);
}
return capitano.execute(cli, callback);
}
], errors.handle);
}).call(this);

View File

@ -1,96 +0,0 @@
[
{
"name": "basic-resin-node-project",
"display_name": "Node.js Starter Project",
"repository": "https://github.com/resin-io/basic-resin-node-project",
"description": "This is a simple Hello, World project for node.js designed to act as a basis for future work. It demonstrates how to install native Linux packages and configure your application."
},
{
"name": "cimon",
"display_name": "Cimon",
"repository": "https://bitbucket.org/efwe/cimon",
"description": "A simple tool for reading temperatures from a USB-enabled thermometer. This project is used as the backend to efwe's awesome temperature visualisation at 123k.de.",
"author": "efwe"
},
{
"name": "firebaseDTL",
"display_name": "Digital Temperature Logger",
"repository": "https://github.com/shaunmulligan/firebaseDTL",
"description": "A Firebase-backed Digital Temperature Logger allowing you to connect devices with multiple temperature sensors to a central cloud-based datastore.",
"author": "Shaun Mulligan"
},
{
"name": "digitiser",
"display_name": "Digitiser",
"repository": "https://github.com/shaunmulligan/digitiser",
"description": "A tool for displaying integer values from a JSON endpoint on a MAX7219 7-segment display.",
"author": "Shaun Mulligan"
},
{
"name": "basic-gpio",
"display_name": "Example Pi Pins Application",
"repository": "https://github.com/shaunmulligan/basic-gpio",
"description": "A simple application which demonstrates the use of the Pi Pins library to interface with GPIO.",
"author": "Shaun Mulligan"
},
{
"name": "coder",
"display_name": "Google Coder",
"repository": "https://github.com/resin-io/coder",
"description": "Resin.io-enabled version of Google's excellent Coder project which makes it easy to develop web projects on your device."
},
{
"name": "hoversnap",
"display_name": "Hoversnap",
"repository": "https://github.com/resin-io/hoversnap",
"description": "A tool for controlling a camera using a foot switch in order to capture shots in which people appear to be flying."
},
{
"name": "resin-piminer",
"display_name": "Pi Miner",
"repository": "https://github.com/csquared/resin-piminer",
"description": "A bitcoin miner for the Raspberry Pi.",
"author": "Chris Continanza"
},
{
"name": "resin-cctv",
"display_name": "Resin CCTV",
"repository": "https://github.com/abresas/resin-cctv",
"description": "A project which allows you to use your devices as a CCTV camera system which hooks into Dropbox.",
"author": "Aleksis Brezas"
},
{
"name": "resin_player",
"display_name": "Resin Player",
"repository": "https://bitbucket.org/lifeeth/resin_player/",
"description": "A project which allows you to play squeezebox media through your devices.",
"author": "Praneeth Bodduluri"
},
{
"name": "salesforceTemp",
"display_name": "Salesforce Temperature Probe",
"repository": "https://github.com/shaunmulligan/salesforceTemp",
"description": "Example application for interfacing with a temperature probe using Salesforce.com.",
"author": "Shaun Mulligan"
},
{
"name": "sms2speech",
"display_name": "SMS to Speech",
"repository": "https://github.com/alexandrosm/sms2speech",
"description": "A simple tool which uses Twillio to read out incoming SMS messages.",
"author": "Alexandros Marinos"
},
{
"name": "resin-kiosk",
"display_name": "Simple Digitiser Kiosk",
"repository": "https://bitbucket.org/lifeeth/resin-kiosk",
"description": "Displays values from a JSON endpoint on your browser in kiosk mode",
"author": "Praneeth Bodduluri"
},
{
"name": "text2speech",
"display_name": "Text to Speech Converter",
"repository": "https://github.com/resin-io/text2speech",
"description": "A simple application that makes your device speak out loud."
}
]

View File

@ -1,30 +0,0 @@
(function() {
var diskio, fs, progressStream;
fs = require('fs');
progressStream = require('progress-stream');
diskio = require('diskio');
exports.name = 'Raspberry Pi';
exports.write = function(options, callback) {
var error, imageFileSize, imageFileStream, progress;
imageFileSize = fs.statSync(options.image).size;
if (imageFileSize === 0) {
error = new Error("Invalid OS image: " + options.image + ". The image is 0 bytes.");
return callback(error);
}
progress = progressStream({
length: imageFileSize,
time: 500
});
if (!options.quiet) {
progress.on('progress', options.progress);
}
imageFileStream = fs.createReadStream(options.image).pipe(progress);
return diskio.writeStream(options.device, imageFileStream, callback);
};
}).call(this);

View File

@ -1,25 +0,0 @@
(function() {
var _, isWindows, os, path;
_ = require('lodash');
os = require('os');
path = require('path');
isWindows = function() {
return os.platform() === 'win32';
};
exports.shouldElevate = function(error) {
return _.all([isWindows(), error.code === 'EPERM' || error.code === 'EACCES']);
};
exports.run = function(command) {
if (!isWindows()) {
return;
}
return require('windosu').exec(command);
};
}).call(this);

View File

@ -1,47 +0,0 @@
(function() {
var _, os;
_ = require('lodash');
os = require('os');
exports.handle = function(error, exit) {
var errorCode, message;
if (exit == null) {
exit = true;
}
if ((error == null) || !(error instanceof Error)) {
return;
}
if (process.env.DEBUG) {
console.error(error.stack);
} else {
if (error.code === 'EISDIR') {
console.error("File is a directory: " + error.path);
} else if (error.code === 'ENOENT') {
console.error("No such file or directory: " + error.path);
} else if (error.code === 'EACCES' || error.code === 'EPERM') {
message = 'You don\'t have enough privileges to run this operation.\n';
if (os.platform() === 'win32') {
message += 'Run a new Command Prompt as administrator and try running this command again.';
} else {
message += 'Try running this command again prefixing it with `sudo`.';
}
console.error(message);
} else if (error.code === 'ENOGIT') {
console.error('Git is not installed on this system.\nHead over to http://git-scm.com to install it and run this command again.');
} else if (error.message != null) {
console.error(error.message);
}
}
if (_.isNumber(error.exitCode)) {
errorCode = error.exitCode;
} else {
errorCode = 1;
}
if (exit) {
return process.exit(errorCode);
}
};
}).call(this);

View File

@ -1,38 +0,0 @@
(function() {
var _, async, npm;
npm = require('npm');
async = require('async');
_ = require('lodash-contrib');
exports.getLatestVersion = function(name, callback) {
return async.waterfall([
function(callback) {
var options;
options = {
loglevel: 'silent',
global: true
};
return npm.load(options, _.unary(callback));
}, function(callback) {
return npm.commands.view([name], true, function(error, data) {
var versions;
versions = _.keys(data);
return callback(error, _.first(versions));
});
}
], callback);
};
exports.isUpdated = function(name, currentVersion, callback) {
return exports.getLatestVersion(name, function(error, latestVersion) {
if (error != null) {
return callback(error);
}
return callback(null, currentVersion === latestVersion);
});
};
}).call(this);

View File

@ -1,55 +0,0 @@
(function() {
var Nplugm, _, capitano, nplugm, registerPlugin;
Nplugm = require('nplugm');
_ = require('lodash');
capitano = require('capitano');
nplugm = null;
registerPlugin = function(plugin) {
if (!_.isArray(plugin)) {
return capitano.command(plugin);
}
return _.each(plugin, capitano.command);
};
exports.register = function(prefix, callback) {
nplugm = new Nplugm(prefix);
return nplugm.list(function(error, plugins) {
var i, len, plugin;
if (error != null) {
return callback(error);
}
for (i = 0, len = plugins.length; i < len; i++) {
plugin = plugins[i];
try {
registerPlugin(nplugm.require(plugin));
} catch (_error) {
error = _error;
console.error(error.message);
}
}
return callback();
});
};
exports.list = function() {
return nplugm.list.apply(nplugm, arguments);
};
exports.install = function() {
return nplugm.install.apply(nplugm, arguments);
};
exports.update = function() {
return nplugm.update.apply(nplugm, arguments);
};
exports.remove = function() {
return nplugm.remove.apply(nplugm, arguments);
};
}).call(this);

View File

@ -1,37 +0,0 @@
(function() {
var packageJSON, updateAction, updateNotifier;
updateNotifier = require('update-notifier');
packageJSON = require('../package.json');
updateAction = require('./actions/update');
exports.perform = function(callback) {
return updateAction.update.action(null, null, callback);
};
exports.notify = function(update) {
if (!process.stdout.isTTY) {
return;
}
return console.log("> Major update available: " + update.current + " -> " + update.latest + "\n> Run resin update to update.\n> Beware that a major release might introduce breaking changes.\n");
};
exports.check = function(callback) {
var notifier;
notifier = updateNotifier({
pkg: packageJSON
});
if (notifier.update == null) {
return callback();
}
if (notifier.update.type === 'major') {
exports.notify(notifier.update);
return callback();
}
console.log("Performing " + notifier.update.type + " update, hold tight...");
return exports.perform(callback);
};
}).call(this);

View File

@ -1,96 +0,0 @@
{
"title": "Resin CLI Documentation",
"introduction": "This tool allows you to interact with the resin.io api from the comfort of your command line.\n\nTo get started download the CLI from npm.\n\n\t$ npm install resin-cli -g\n\nThen authenticate yourself:\n\n\t$ resin login\n\nNow you have access to all the commands referenced below.",
"categories": [
{
"title": "Application",
"files": [
"lib/actions/app.coffee"
]
},
{
"title": "Authentication",
"files": [
"lib/actions/auth.coffee"
]
},
{
"title": "Device",
"files": [
"lib/actions/device.coffee"
]
},
{
"title": "Drive",
"files": [
"lib/actions/drive.coffee"
]
},
{
"title": "Environment Variables",
"files": [
"lib/actions/environment-variables.coffee"
]
},
{
"title": "Examples",
"files": [
"lib/actions/examples.coffee"
]
},
{
"title": "Help",
"files": [
"lib/actions/help.coffee"
]
},
{
"title": "Information",
"files": [
"lib/actions/info.coffee"
]
},
{
"title": "Keys",
"files": [
"lib/actions/keys.coffee"
]
},
{
"title": "Logs",
"files": [
"lib/actions/logs.coffee"
]
},
{
"title": "Notes",
"files": [
"lib/actions/notes.coffee"
]
},
{
"title": "OS",
"files": [
"lib/actions/os.coffee"
]
},
{
"title": "Plugin",
"files": [
"lib/actions/plugin.coffee"
]
},
{
"title": "Preferences",
"files": [
"lib/actions/preferences.coffee"
]
},
{
"title": "Update",
"files": [
"lib/actions/update.coffee"
]
}
]
}

154
capitanodoc.ts Normal file
View File

@ -0,0 +1,154 @@
export = {
title: 'Resin CLI Documentation',
introduction: `\
This tool allows you to interact with the resin.io api from the comfort of your command line.
Please make sure your system meets the requirements as specified in the [README](https://github.com/resin-io/resin-cli).
## Install the CLI
### Npm install
The best supported way to install the CLI is from npm:
$ npm install resin-cli -g --production --unsafe-perm
\`--unsafe-perm\` is only required on systems where the global install directory is not user-writable.
This allows npm install steps to download and save prebuilt native binaries. You may be able to omit it,
especially if you're using a user-managed node install such as [nvm](https://github.com/creationix/nvm).
### Standalone install
Alternatively, if you don't have a node or pre-gyp environment, you can still install the CLI as a standalone
binary. **This is in experimental and may not work perfectly yet in all environments**, but works well in
initial cross-platform testing, so it may be useful, and we'd love your feedback if you hit any issues.
To install the CLI as a standalone binary:
* Download the latest zip for your OS from https://github.com/resin-io/resin-cli/releases.
* Extract the contents, putting the \`resin-cli\` folder somewhere appropriate for your system (e.g. \`C:/resin-cli\`, \`/usr/local/lib/resin-cli\`, etc).
* Add the \`resin-cli\` folder to your \`PATH\`. (
[Windows instructions](https://www.computerhope.com/issues/ch000549.htm),
[Linux instructions](https://stackoverflow.com/questions/14637979/how-to-permanently-set-path-on-linux-unix),
[OSX instructions](https://stackoverflow.com/questions/22465332/setting-path-environment-variable-in-osx-permanently))
* Running \`resin\` in a fresh command line should print the resin CLI help.
To update in future, simply download a new release and replace the extracted folder.
Have any problems, or see any unexpected behaviour? Please file an issue!
## Getting started
Once you have the CLI installed, you'll need to log in, so it can access everything in your resin.io account.
To authenticate yourself, run:
$ resin login
You now have access to all the commands referenced below.
## Proxy support
The CLI does support HTTP(S) proxies.
You can configure the proxy using several methods (in order of their precedence):
* set the \`RESINRC_PROXY\` environment variable in the URL format (with protocol, host, port, and optionally the basic auth),
* use the [resin config file](https://www.npmjs.com/package/resin-settings-client#documentation) (project-specific or user-level)
and set the \`proxy\` setting. This can be:
* a string in the URL format,
* or an object following [this format](https://www.npmjs.com/package/global-tunnel-ng#options), which allows more control,
* or set the conventional \`https_proxy\` / \`HTTPS_PROXY\` / \`http_proxy\` / \`HTTP_PROXY\`
environment variable (in the same standard URL format).\
`,
categories: [
{
title: 'Api keys',
files: [ 'build/actions/api-key.js' ],
},
{
title: 'Application',
files: [ 'build/actions/app.js' ]
},
{
title: 'Authentication',
files: [ 'build/actions/auth.js' ]
},
{
title: 'Device',
files: [ 'build/actions/device.js' ]
},
{
title: 'Environment Variables',
files: [ 'build/actions/environment-variables.js' ]
},
{
title: 'Help',
files: [ 'build/actions/help.js' ]
},
{
title: 'Information',
files: [ 'build/actions/info.js' ]
},
{
title: 'Keys',
files: [ 'build/actions/keys.js' ]
},
{
title: 'Logs',
files: [ 'build/actions/logs.js' ]
},
{
title: 'Sync',
files: [ 'build/actions/sync.js' ]
},
{
title: 'SSH',
files: [ 'build/actions/ssh.js' ]
},
{
title: 'Notes',
files: [ 'build/actions/notes.js' ]
},
{
title: 'OS',
files: [ 'build/actions/os.js' ]
},
{
title: 'Config',
files: [ 'build/actions/config.js' ]
},
{
title: 'Preload',
files: [ 'build/actions/preload.js' ]
},
{
title: 'Push',
files: [ 'build/actions/push.js' ]
},
{
title: 'Settings',
files: [ 'build/actions/settings.js' ]
},
{
title: 'Wizard',
files: [ 'build/actions/wizard.js' ]
},
{
title: 'Local',
files: [ 'build/actions/local/index.js' ]
},
{
title: 'Deploy',
files: [
'build/actions/build.js',
'build/actions/deploy.js'
]
},
{
title: 'Utilities',
files: [ 'build/actions/util.js' ]
},
]
};

View File

@ -1,49 +0,0 @@
_resin() {
COMPREPLY=()
local current="${COMP_WORDS[COMP_CWORD]}"
local previous="${COMP_WORDS[COMP_CWORD-1]}"
local options="version help login logout signup drive whoami app apps init devices device note preferences keys key envs env logs os examples example"
case "${previous}" in
app)
local subcommands="create rm restart"
COMPREPLY=( $(compgen -W "${subcommands}" -- ${current}) )
return 0 ;;
drive)
local subcommands="list"
COMPREPLY=( $(compgen -W "${subcommands}" -- ${current}) )
return 0 ;;
devices)
local subcommands="supported"
COMPREPLY=( $(compgen -W "${subcommands}" -- ${current}) )
return 0 ;;
device)
local subcommands="rename rm identify init"
COMPREPLY=( $(compgen -W "${subcommands}" -- ${current}) )
return 0 ;;
key)
local subcommands="add rm"
COMPREPLY=( $(compgen -W "${subcommands}" -- ${current}) )
return 0 ;;
env)
local subcommands="add rename rm"
COMPREPLY=( $(compgen -W "${subcommands}" -- ${current}) )
return 0 ;;
os)
local subcommands="download install"
COMPREPLY=( $(compgen -W "${subcommands}" -- ${current}) )
return 0 ;;
example)
local subcommands="clone"
COMPREPLY=( $(compgen -W "${subcommands}" -- ${current}) )
return 0 ;;
*)
;;
esac
COMPREPLY=( $(compgen -W "${options}" -- ${current}) )
return 0
}
complete -F _resin resin

112
doc/automated-init.md Normal file
View File

@ -0,0 +1,112 @@
# Provisioning Resin.io devices in automated (non-interactive) mode
This document describes how to run the `device init` command in non-interactive mode.
It requires collecting some preliminary information _once_.
The final command to provision the device looks like this:
```bash
resin device init --app APP_ID --os-version OS_VERSION --drive DRIVE --config CONFIG_FILE --yes
```
You can run this command as many times as you need, putting the new medium (SD card / USB stick) each time.
But before you can run it you need to collect the parameters and build the configuration file. Keep reading to figure out how to do it.
## Collect all the required parameters.
1. `DEVICE_TYPE`. Run
```bash
resin devices supported
```
and find the _slug_ for your target device type, like _raspberrypi3_.
1. `APP_ID`. Create an application (`resin app create APP_NAME --type DEVICE_TYPE`) or find an existing one (`resin apps`) and notice its ID.
1. `OS_VERSION`. Run
```bash
resin os versions DEVICE_TYPE
```
and pick the version that you need, like _v2.0.6+rev1.prod_.
_Note_ that even though we support _semver ranges_ it's recommended to use the exact version when doing the automated provisioning as it
guarantees full compatibility between the steps.
1. `DRIVE`. Plug in your target medium (SD card or the USB stick, depending on your device type) and run
```bash
resin util available-drives
```
and get the drive name, like _/dev/sdb_ or _/dev/mmcblk0_.
The resin CLI will not display the system drives to protect you,
but still please check very carefully that you've picked the correct drive as it will be erased during the provisioning process.
Now we have all the parameters -- time to build the config file.
## Build the config file
Interactive device provisioning process often includes collecting some extra device configuration, like the networking mode and wifi credentials.
To skip this interactive step we need to buid this configuration once and save it to the JSON file for later reuse.
Let's say we will place it into the `CONFIG_FILE` path, like _./resin-os/raspberrypi3-config.json_.
We also need to put the OS image somewhere, let's call this path `OS_IMAGE_PATH`, it can be something like _./resin-os/raspberrypi3-v2.0.6+rev1.prod.img_.
1. First we need to download the OS image once. That's needed for building the config, and will speedup the subsequent operations as the downloaded OS image is placed into the local cache.
Run:
```bash
resin os download DEVICE_TYPE --output OS_IMAGE_PATH --version OS_VERSION
```
1. Now we're ready to build the config:
```bash
resin os build-config OS_IMAGE_PATH DEVICE_TYPE --output CONFIG_FILE
```
This will run you through the interactive configuration wizard and in the end save the generated config as `CONFIG_FILE`. You can then verify it's not empty:
```bash
cat CONFIG_FILE
```
## Done
Now you're ready to run the command in the beginning of this guide.
Please note again that all of these steps only need to be done once (unless you need to change something), and once all the parameters are collected the main init command can be run unchanged.
But there are still some nuances to cover, please read below.
## Nuances
### `sudo` password on *nix systems
In order to write the image to the raw device we need the root permissions, this is unavoidable.
To improve the security we only run the minimal subcommand with `sudo`.
This means that with the default setup you're interrupted closer to the end of the device init process to enter your sudo password for this subcommand to work.
There are several ways to eliminate it and make the process fully non-interactive.
#### Option 1: make passwordless sudo.
Obviously you shouldn't do that if the machine you're working on has access to any sensitive resources or information.
But if you're using a machine dedicated to resin provisioning this can be fine, and also the simplest thing to do.
#### Option 2: `NOPASSWD` directive
You can configure the `resin` CLI command to be sudo-runnable without the password. Check [this post](https://askubuntu.com/questions/159007/how-do-i-run-specific-sudo-commands-without-a-password) for an example.
### Extra initialization config
As of June 2017 all the supported devices should not require any other interactive configuration.
But by the design of our system it is _possible_ (though it doesn't look very likely it's going to happen any time soon) that some extra initialization options may be requested for the specific device types.
If that is the case please raise the issue in the resin CLI repository and the maintainers will add the necessary options to build the similar JSON config for this step.

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +0,0 @@
_ = require('lodash')
capitanodoc = require('../../capitanodoc.json')
markdown = require('./markdown')
result = {}
result.title = capitanodoc.title
result.introduction = capitanodoc.introduction
result.categories = []
for commandCategory in capitanodoc.categories
category = {}
category.title = commandCategory.title
category.commands = []
for file in commandCategory.files
actions = require(file)
for actionName, actionCommand of actions
category.commands.push(_.omit(actionCommand, 'action'))
result.categories.push(category)
result.toc = _.cloneDeep(result.categories)
result.toc = _.map result.toc, (category) ->
category.commands = _.map category.commands, (command) ->
return {
signature: command.signature
# TODO: Make anchor prefix a configurable setting
# in capitanodoc.json
anchor: '#/pages/using/cli.md#' + command.signature
.replace(/\s/g,'-')
.replace(/</g, '60-')
.replace(/>/g, '-62-')
.replace(/\[/g, '')
.replace(/\]/g, '-')
.replace(/--/g, '-')
.replace(/\.\.\./g, '')
.replace(/\|/g, '')
.toLowerCase()
}
return category
console.log(markdown.display(result))

View File

@ -1,66 +0,0 @@
_ = require('lodash')
ent = require('ent')
utils = require('./utils')
exports.command = (command) ->
result = """
## #{ent.encode(command.signature)}
#{command.help}\n
"""
if not _.isEmpty(command.options)
result += '\n### Options'
for option in command.options
result += """
\n\n#### #{utils.parseSignature(option)}
#{option.description}
"""
result += '\n'
return result
exports.category = (category) ->
result = """
# #{category.title}\n
"""
for command in category.commands
result += '\n' + exports.command(command)
return result
exports.toc = (toc) ->
result = '''
# Table of contents\n
'''
for category in toc
result += """
\n- #{category.title}\n\n
"""
for command in category.commands
result += """
\t- [#{ent.encode(command.signature)}](#{command.anchor})\n
"""
return result
exports.display = (doc) ->
result = """
# #{doc.title}
#{doc.introduction}
#{exports.toc(doc.toc)}
"""
for category in doc.categories
result += '\n' + exports.category(category)
return result

View File

@ -1,26 +0,0 @@
_ = require('lodash')
ent = require('ent')
exports.getOptionPrefix = (signature) ->
if signature.length > 1
return '--'
else
return '-'
exports.getOptionSignature = (signature) ->
return "#{exports.getOptionPrefix(signature)}#{signature}"
exports.parseSignature = (option) ->
result = exports.getOptionSignature(option.signature)
if not _.isEmpty(option.alias)
if _.isString(option.alias)
result += ", #{exports.getOptionSignature(option.alias)}"
else
for alias in option.alias
result += ", #{exports.getOptionSignature(option.alias)}"
if option.parameter?
result += " <#{option.parameter}>"
return ent.encode(result)

View File

@ -1,59 +1,40 @@
mkdirp = require('mkdirp')
path = require('path')
gulp = require('gulp')
mocha = require('gulp-mocha')
coffee = require('gulp-coffee')
markedMan = require('gulp-marked-man')
coffeelint = require('gulp-coffeelint')
inlinesource = require('gulp-inline-source')
mocha = require('gulp-mocha')
shell = require('gulp-shell')
mochaNotifierReporter = require('mocha-notifier-reporter')
packageJSON = require('./package.json')
OPTIONS =
config:
coffeelint: path.join(__dirname, 'coffeelint.json')
files:
coffee: [ 'lib/**/*.coffee', 'gulpfile.coffee' ]
app: [ 'lib/**/*.coffee', '!lib/**/*.spec.coffee' ]
app: 'lib/**/*.coffee'
tests: 'tests/**/*.spec.coffee'
json: [ 'lib/**/*.json' ]
man: 'man/**/*.md'
pages: 'lib/auth/pages/*.ejs'
directories:
man: 'man/'
build: 'build/'
gulp.task 'man', ->
gulp.src(OPTIONS.files.man)
.pipe(markedMan())
.pipe(gulp.dest(OPTIONS.directories.man))
gulp.task 'pages', ->
gulp.src(OPTIONS.files.pages)
.pipe(inlinesource())
.pipe(gulp.dest('build/auth/pages'))
gulp.task 'coffee', ->
gulp.src(OPTIONS.files.app)
.pipe(coffee(bare: true, header: true))
.pipe(gulp.dest(OPTIONS.directories.build))
gulp.task 'test', ->
gulp.src(OPTIONS.files.tests, read: false)
.pipe(mocha({
reporter: mochaNotifierReporter.decorate('landing')
reporter: 'spec'
}))
gulp.task 'coffee', [ 'test', 'lint', 'json' ], ->
gulp.src(OPTIONS.files.app)
.pipe(coffee())
.pipe(gulp.dest(OPTIONS.directories.build))
gulp.task 'json', ->
gulp.src(OPTIONS.files.json)
.pipe(gulp.dest(OPTIONS.directories.build))
gulp.task 'lint', ->
gulp.src(OPTIONS.files.coffee)
.pipe(coffeelint({
optFile: OPTIONS.config.coffeelint
}))
.pipe(coffeelint.reporter())
gulp.task 'build', [
'coffee'
'man'
'coffee',
'pages'
]
gulp.task 'watch', [ 'test', 'lint' ], ->
gulp.watch([ OPTIONS.files.coffee, OPTIONS.files.json ], [ 'coffee' ])
gulp.watch([ OPTIONS.files.man ], [ 'man' ])
gulp.task 'watch', [ 'build' ], ->
gulp.watch([ OPTIONS.files.coffee ], [ 'build' ])

36
lib/actions/api-key.ts Normal file
View File

@ -0,0 +1,36 @@
import { CommandDefinition } from 'capitano';
import { stripIndent } from 'common-tags';
export const generate: CommandDefinition<{
name: string;
}> = {
signature: 'api-key generate <name>',
description: 'Generate a new API key with the given name',
help: stripIndent`
This command generates a new API key for the current user, with the given
name. The key will be logged to the console.
This key can be used to log into the CLI using 'resin login --token <key>',
or to authenticate requests to the API with an 'Authorization: Bearer <key>' header.
Examples:
$ resin api-key generate "Jenkins Key"
`,
async action(params, _options, done) {
const resin = (await import('resin-sdk')).fromSharedOptions();
resin.models.apiKey
.create(params.name)
.then(key => {
console.log(stripIndent`
Registered api key '${params.name}':
${key}
This key will not be shown again, so please save it now.
`);
})
.finally(done);
},
};

View File

@ -1,10 +1,20 @@
path = require('path')
_ = require('lodash-contrib')
async = require('async')
resin = require('resin-sdk')
visuals = require('resin-cli-visuals')
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
commandOptions = require('./command-options')
vcs = require('resin-vcs')
exports.create =
signature: 'app create <name>'
@ -12,7 +22,7 @@ exports.create =
help: '''
Use this command to create a new resin.io application.
You can specify the application type with the `--type` option.
You can specify the application device type with the `--type` option.
Otherwise, an interactive dropdown will be shown for you to select from.
You can see a list of supported device types with
@ -28,25 +38,29 @@ exports.create =
{
signature: 'type'
parameter: 'type'
description: 'application type'
description: 'application device type (Check available types with `resin devices supported`)'
alias: 't'
}
]
permission: 'user'
action: (params, options, done) ->
async.waterfall([
resin = require('resin-sdk-preconfigured')
patterns = require('../utils/patterns')
(callback) ->
return callback(null, options.type) if options.type?
# Validate the the application name is available
# before asking the device type.
# https://github.com/resin-io/resin-cli/issues/30
resin.models.application.has(params.name).then (hasApplication) ->
if hasApplication
patterns.exitWithExpectedError('You already have an application with that name!')
resin.models.device.getSupportedDeviceTypes (error, deviceTypes) ->
return callback(error) if error?
visuals.widgets.select('Select a type', deviceTypes, callback)
(type, callback) ->
resin.models.application.create(params.name, type, callback)
], done)
.then ->
return options.type or patterns.selectDeviceType()
.then (deviceType) ->
return resin.models.application.create(params.name, deviceType)
.then (application) ->
console.info("Application created: #{application.app_name} (#{application.device_type}, id #{application.id})")
.nodeify(done)
exports.list =
signature: 'apps'
@ -62,17 +76,20 @@ exports.list =
$ resin apps
'''
permission: 'user'
primary: true
action: (params, options, done) ->
resin.models.application.getAll (error, applications) ->
return done(error) if error?
console.log visuals.widgets.table.horizontal applications, [
resin = require('resin-sdk-preconfigured')
visuals = require('resin-cli-visuals')
resin.models.application.getAll().then (applications) ->
console.log visuals.table.horizontal applications, [
'id'
'app_name'
'device_type'
'online_devices'
'devices_length'
]
return done()
.nodeify(done)
exports.info =
signature: 'app <name>'
@ -85,17 +102,20 @@ exports.info =
$ resin app MyApp
'''
permission: 'user'
primary: true
action: (params, options, done) ->
resin.models.application.get params.name, (error, application) ->
return done(error) if error?
console.log visuals.widgets.table.vertical application, [
resin = require('resin-sdk-preconfigured')
visuals = require('resin-cli-visuals')
resin.models.application.get(params.name).then (application) ->
console.log visuals.table.vertical application, [
"$#{application.app_name}$"
'id'
'app_name'
'device_type'
'git_repository'
'commit'
]
return done()
.nodeify(done)
exports.restart =
signature: 'app restart <name>'
@ -109,7 +129,8 @@ exports.restart =
'''
permission: 'user'
action: (params, options, done) ->
resin.models.application.restart(params.name, done)
resin = require('resin-sdk-preconfigured')
resin.models.application.restart(params.name).nodeify(done)
exports.remove =
signature: 'app rm <name>'
@ -128,79 +149,9 @@ exports.remove =
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
visuals.patterns.remove 'application', options.yes, (callback) ->
resin.models.application.remove(params.name, callback)
, done
resin = require('resin-sdk-preconfigured')
patterns = require('../utils/patterns')
exports.associate =
signature: 'app associate <name>'
description: 'associate a resin project'
help: '''
Use this command to associate a project directory with a resin application.
This command adds a 'resin' git remote to the directory and runs git init if necessary.
Examples:
$ resin app associate MyApp
$ resin app associate MyApp --project my/app/directory
'''
permission: 'user'
action: (params, options, done) ->
currentDirectory = process.cwd()
async.waterfall [
(callback) ->
vcs.initialize(currentDirectory, callback)
(callback) ->
resin.models.application.get(params.name, callback)
(application, callback) ->
vcs.addRemote(currentDirectory, application.git_repository, callback)
], (error, remoteUrl) ->
return done(error) if error?
console.info("git repository added: #{remoteUrl}")
return done(null, remoteUrl)
exports.init =
signature: 'init'
description: 'init an application'
help: '''
Use this command to initialise a directory as a resin application.
This command performs the following steps:
- Create a resin.io application.
- Initialize the current directory as a git repository.
- Add the corresponding git remote to the application.
Examples:
$ resin init
$ resin init --project my/app/directory
'''
permission: 'user'
action: (params, options, done) ->
currentDirectory = process.cwd()
async.waterfall([
(callback) ->
currentDirectoryBasename = path.basename(currentDirectory)
visuals.widgets.ask('What is the name of your application?', currentDirectoryBasename, callback)
(applicationName, callback) ->
# TODO: Make resin.models.application.create return
# the whole application instead of just the id
exports.create.action name: applicationName, options, (error) ->
return callback(error) if error?
return callback(null, applicationName)
(applicationName, callback) ->
exports.associate.action(name: applicationName, options, callback)
], done)
patterns.confirm(options.yes, 'Are you sure you want to delete the application?').then ->
resin.models.application.remove(params.name)
.nodeify(done)

View File

@ -1,73 +1,129 @@
open = require('open')
_ = require('lodash-contrib')
url = require('url')
async = require('async')
resin = require('resin-sdk')
settings = require('resin-settings-client')
visuals = require('resin-cli-visuals')
###
Copyright 2016-2017 Resin.io
exports.whoami =
signature: 'whoami'
description: 'whoami'
help: '''
Use this command to get the logged in user name.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Examples:
http://www.apache.org/licenses/LICENSE-2.0
$ resin whoami
'''
permission: 'user'
action: (params, options, done) ->
resin.auth.whoami (error, username) ->
return done(error) if error?
console.log(username)
return done()
TOKEN_URL = url.resolve(settings.get('remoteUrl'), '/preferences')
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
exports.login =
signature: 'login [token]'
signature: 'login'
description: 'login to resin.io'
help: """
help: '''
Use this command to login to your resin.io account.
To login, you need your token, which is accesible from the preferences page:
This command will prompt you to login using the following login types:
#{TOKEN_URL}
- Web authorization: open your web browser and prompt you to authorize the CLI
from the dashboard.
- Credentials: using email/password and 2FA.
- Token: using a session token or API key (experimental) from the preferences page.
Examples:
$ resin login
$ resin login "eyJ0eXAiOiJKV1Qi..."
"""
$ resin login --web
$ resin login --token "..."
$ resin login --credentials
$ resin login --credentials --email johndoe@gmail.com --password secret
'''
options: [
{
signature: 'token'
description: 'session token or API key (experimental)'
parameter: 'token'
alias: 't'
}
{
signature: 'web'
description: 'web-based login'
boolean: true
alias: 'w'
}
{
signature: 'credentials'
description: 'credential-based login'
boolean: true
alias: 'c'
}
{
signature: 'email'
parameter: 'email'
description: 'email'
alias: [ 'e', 'u' ]
}
{
signature: 'password'
parameter: 'password'
description: 'password'
alias: 'p'
}
]
primary: true
action: (params, options, done) ->
_ = require('lodash')
Promise = require('bluebird')
resin = require('resin-sdk').fromSharedOptions()
auth = require('../auth')
form = require('resin-cli-form')
patterns = require('../utils/patterns')
messages = require('../utils/messages')
console.info """
To login to the Resin CLI, you need your unique token, which is accesible from
the preferences page at #{TOKEN_URL}
login = (options) ->
if options.token?
return Promise.try ->
return options.token if _.isString(options.token)
return form.ask
message: 'Session token or API key (experimental) from the preferences page'
name: 'token'
type: 'input'
.then(resin.auth.loginWithToken)
.tap ->
resin.auth.whoami()
.then (username) ->
if !username
patterns.exitWithExpectedError('Token authentication failed')
else if options.credentials
return patterns.authenticate(options)
else if options.web
console.info('Connecting to the web dashboard')
return auth.login()
Attempting to open a browser at that location...
"""
return patterns.askLoginType().then (loginType) ->
async.waterfall([
if loginType is 'register'
{ runCommand } = require('../utils/helpers')
return runCommand('signup')
(callback) ->
open TOKEN_URL, (error) ->
if error?
console.error """
Unable to open a web browser in the current environment.
Please visit #{TOKEN_URL} manually.
"""
return callback()
options[loginType] = true
return login(options)
(callback) ->
return callback(null, params.token) if params.token?
visuals.widgets.ask('What\'s your token? (visible in the preferences page)', null, callback)
resin.settings.get('resinUrl').then (resinUrl) ->
console.log(messages.resinAsciiArt)
console.log("\nLogging in to #{resinUrl}")
return login(options)
.then(resin.auth.whoami)
.tap (username) ->
console.info("Successfully logged in as: #{username}")
console.info """
(token, callback) ->
resin.auth.loginWithToken(token, done)
Find out about the available commands by running:
], done)
$ resin help
#{messages.reachingOut}
"""
.nodeify(done)
exports.logout =
signature: 'logout'
@ -79,9 +135,9 @@ exports.logout =
$ resin logout
'''
permission: 'user'
action: (params, options, done) ->
resin.auth.logout(done)
resin = require('resin-sdk').fromSharedOptions()
resin.auth.logout().nodeify(done)
exports.signup =
signature: 'signup'
@ -94,70 +150,41 @@ exports.signup =
Examples:
$ resin signup
Email: me@mycompany.com
Username: johndoe
Email: johndoe@acme.com
Password: ***********
$ resin signup --email me@mycompany.com --username johndoe --password ***********
$ resin whoami
johndoe
'''
options: [
{
signature: 'email'
parameter: 'email'
description: 'user email'
alias: 'e'
}
{
signature: 'username'
parameter: 'username'
description: 'user name'
alias: 'u'
}
{
signature: 'password'
parameter: 'user password'
description: 'user password'
alias: 'p'
}
]
action: (params, options, done) ->
resin = require('resin-sdk').fromSharedOptions()
form = require('resin-cli-form')
validation = require('../utils/validation')
hasOptionCredentials = not _.isEmpty(options)
resin.settings.get('resinUrl').then (resinUrl) ->
console.log("\nRegistering to #{resinUrl}")
if hasOptionCredentials
form.run [
message: 'Email:'
name: 'email'
type: 'input'
validate: validation.validateEmail
,
message: 'Password:'
name: 'password'
type: 'password',
validate: validation.validatePassword
]
if not options.email?
return done(new Error('Missing email'))
if not options.username?
return done(new Error('Missing username'))
if not options.password?
return done(new Error('Missing password'))
async.waterfall([
(callback) ->
return callback(null, options) if hasOptionCredentials
visuals.widgets.register(callback)
(credentials, callback) ->
resin.auth.register credentials, (error, token) ->
return callback(error, credentials)
(credentials, callback) ->
resin.auth.login(credentials, callback)
], done)
.then(resin.auth.register)
.then(resin.auth.loginWithToken)
.nodeify(done)
exports.whoami =
signature: 'whoami'
description: 'get current username'
description: 'get current username and email address'
help: '''
Use this command to find out the current logged in username.
Use this command to find out the current logged in username and email address.
Examples:
@ -165,9 +192,19 @@ exports.whoami =
'''
permission: 'user'
action: (params, options, done) ->
resin.auth.whoami (error, username) ->
Promise = require('bluebird')
resin = require('resin-sdk').fromSharedOptions()
visuals = require('resin-cli-visuals')
if not username?
return done(new Error('Username not found'))
console.log(username)
Promise.props
username: resin.auth.whoami()
email: resin.auth.getEmail()
url: resin.settings.get('resinUrl')
.then (results) ->
console.log visuals.table.vertical results, [
'$account information$'
'username'
'email'
'url'
]
.nodeify(done)

145
lib/actions/build.coffee Normal file
View File

@ -0,0 +1,145 @@
# Imported here because it's needed for the setup
# of this action
Promise = require('bluebird')
dockerUtils = require('../utils/docker')
compose = require('../utils/compose')
###
Opts must be an object with the following keys:
app: the app this build is for (optional)
arch: the architecture to build for
deviceType: the device type to build for
buildEmulated
buildOpts: arguments to forward to docker build command
###
buildProject = (docker, logger, composeOpts, opts) ->
compose.loadProject(
logger
composeOpts.projectPath
composeOpts.projectName
)
.then (project) ->
appType = opts.app?.application_type?[0]
if appType? and project.descriptors.length > 1 and not appType.supports_multicontainer
logger.logWarn(
'Target application does not support multiple containers.\n' +
'Continuing with build, but you will not be able to deploy.'
)
compose.buildProject(
docker
logger
project.path
project.name
project.composition
opts.arch
opts.deviceType
opts.buildEmulated
opts.buildOpts
composeOpts.inlineLogs
)
.then ->
logger.logSuccess('Build succeeded!')
.tapCatch (e) ->
logger.logError('Build failed')
module.exports =
signature: 'build [source]'
description: 'Build a single image or a multicontainer project locally'
primary: true
help: '''
Use this command to build an image or a complete multicontainer project
with the provided docker daemon.
You must provide either an application or a device-type/architecture
pair to use the resin Dockerfile pre-processor
(e.g. Dockerfile.template -> Dockerfile).
This command will look into the given source directory (or the current working
directory if one isn't specified) for a compose file. If one is found, this
command will build each service defined in the compose file. If a compose file
isn't found, the command will look for a Dockerfile, and if yet that isn't found,
it will try to generate one.
Examples:
$ resin build
$ resin build ./source/
$ resin build --deviceType raspberrypi3 --arch armhf --emulated
$ resin build --application MyApp ./source/
$ resin build --docker '/var/run/docker.sock'
$ resin build --dockerHost my.docker.host --dockerPort 2376 --ca ca.pem --key key.pem --cert cert.pem
'''
options: dockerUtils.appendOptions compose.appendOptions [
{
signature: 'arch'
parameter: 'arch'
description: 'The architecture to build for'
alias: 'A'
},
{
signature: 'deviceType'
parameter: 'deviceType'
description: 'The type of device this build is for'
alias: 'd'
},
{
signature: 'application'
parameter: 'application'
description: 'The target resin.io application this build is for'
alias: 'a'
},
]
action: (params, options, done) ->
# compositions with many services trigger misleading warnings
require('events').defaultMaxListeners = 1000
{ exitWithExpectedError } = require('../utils/patterns')
helpers = require('../utils/helpers')
Logger = require('../utils/logger')
logger = new Logger()
logger.logDebug('Parsing input...')
Promise.try ->
# `build` accepts `[source]` as a parameter, but compose expects it
# as an option. swap them here
options.source ?= params.source
delete params.source
{ application, arch, deviceType } = options
if (not (arch? and deviceType?) and not application?) or (application? and (arch? or deviceType?))
exitWithExpectedError('You must specify either an application or an arch/deviceType pair to build for')
if arch? and deviceType?
[ undefined, arch, deviceType ]
else
Promise.join(
helpers.getApplication(application)
helpers.getArchAndDeviceType(application)
(app, { arch, device_type }) ->
app.arch = arch
app.device_type = device_type
return app
)
.then (app) ->
[ app, app.arch, app.device_type ]
.then ([ app, arch, deviceType ]) ->
Promise.join(
dockerUtils.getDocker(options)
dockerUtils.generateBuildOpts(options)
compose.generateOpts(options)
(docker, buildOpts, composeOpts) ->
buildProject(docker, logger, composeOpts, {
app
arch
deviceType
buildEmulated: !!options.emulated
buildOpts
})
)
.asCallback(done)

View File

@ -1,3 +1,19 @@
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
_ = require('lodash')
exports.yes =
@ -16,6 +32,40 @@ exports.application = _.defaults
required: 'You have to specify an application'
, exports.optionalApplication
exports.optionalDevice =
signature: 'device'
parameter: 'device'
description: 'device uuid'
alias: 'd'
exports.optionalDeviceApiKey =
signature: 'deviceApiKey'
description: 'custom device key - note that this is only supported on ResinOS 2.0.3+'
parameter: 'device-api-key'
alias: 'k'
exports.optionalOsVersion =
signature: 'version'
description: 'a resinOS version'
parameter: 'version'
exports.booleanDevice =
signature: 'device'
description: 'device'
boolean: true
alias: 'd'
exports.osVersion =
signature: 'version'
description: """
exact version number, or a valid semver range,
or 'latest' (includes pre-releases),
or 'default' (excludes pre-releases if at least one stable version is available),
or 'recommended' (excludes pre-releases, will fail if only pre-release versions are available),
or 'menu' (will show the interactive menu)
"""
parameter: 'version'
exports.network =
signature: 'network'
parameter: 'network'
@ -33,3 +83,29 @@ exports.wifiKey =
parameter: 'key'
description: 'wifi key, if network is wifi'
alias: 'k'
exports.forceUpdateLock =
signature: 'force'
description: 'force action if the update lock is set'
boolean: true
alias: 'f'
exports.drive =
signature: 'drive'
description: 'the drive to write the image to, like `/dev/sdb` or `/dev/mmcblk0`.
Careful with this as you can erase your hard drive.
Check `resin util available-drives` for available options.'
parameter: 'drive'
alias: 'd'
exports.advancedConfig =
signature: 'advanced'
description: 'show advanced configuration options'
boolean: true
alias: 'v'
exports.hostOSAccess =
signature: 'host'
boolean: true
description: 'access host OS (for devices with Resin OS >= 2.7.5)'
alias: 's'

328
lib/actions/config.coffee Normal file
View File

@ -0,0 +1,328 @@
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
commandOptions = require('./command-options')
{ normalizeUuidProp } = require('../utils/normalization')
exports.read =
signature: 'config read'
description: 'read a device configuration'
help: '''
Use this command to read the config.json file from the mounted filesystem (e.g. SD card) of a provisioned device"
Examples:
$ resin config read --type raspberry-pi
$ resin config read --type raspberry-pi --drive /dev/disk2
'''
options: [
{
signature: 'type'
description: 'device type (Check available types with `resin devices supported`)'
parameter: 'type'
alias: 't'
required: 'You have to specify a device type'
}
{
signature: 'drive'
description: 'drive'
parameter: 'drive'
alias: 'd'
}
]
permission: 'user'
root: true
action: (params, options, done) ->
Promise = require('bluebird')
config = require('resin-config-json')
visuals = require('resin-cli-visuals')
umountAsync = Promise.promisify(require('umount').umount)
prettyjson = require('prettyjson')
Promise.try ->
return options.drive or visuals.drive('Select the device drive')
.tap(umountAsync)
.then (drive) ->
return config.read(drive, options.type)
.tap (configJSON) ->
console.info(prettyjson.render(configJSON))
.nodeify(done)
exports.write =
signature: 'config write <key> <value>'
description: 'write a device configuration'
help: '''
Use this command to write the config.json file to the mounted filesystem (e.g. SD card) of a provisioned device
Examples:
$ resin config write --type raspberry-pi username johndoe
$ resin config write --type raspberry-pi --drive /dev/disk2 username johndoe
$ resin config write --type raspberry-pi files.network/settings "..."
'''
options: [
{
signature: 'type'
description: 'device type (Check available types with `resin devices supported`)'
parameter: 'type'
alias: 't'
required: 'You have to specify a device type'
}
{
signature: 'drive'
description: 'drive'
parameter: 'drive'
alias: 'd'
}
]
permission: 'user'
root: true
action: (params, options, done) ->
Promise = require('bluebird')
_ = require('lodash')
config = require('resin-config-json')
visuals = require('resin-cli-visuals')
umountAsync = Promise.promisify(require('umount').umount)
Promise.try ->
return options.drive or visuals.drive('Select the device drive')
.tap(umountAsync)
.then (drive) ->
config.read(drive, options.type).then (configJSON) ->
console.info("Setting #{params.key} to #{params.value}")
_.set(configJSON, params.key, params.value)
return configJSON
.tap ->
return umountAsync(drive)
.then (configJSON) ->
return config.write(drive, options.type, configJSON)
.tap ->
console.info('Done')
.nodeify(done)
exports.inject =
signature: 'config inject <file>'
description: 'inject a device configuration file'
help: '''
Use this command to inject a config.json file to the mounted filesystem
(e.g. SD card or mounted resinOS image) of a provisioned device"
Examples:
$ resin config inject my/config.json --type raspberry-pi
$ resin config inject my/config.json --type raspberry-pi --drive /dev/disk2
'''
options: [
{
signature: 'type'
description: 'device type (Check available types with `resin devices supported`)'
parameter: 'type'
alias: 't'
required: 'You have to specify a device type'
}
{
signature: 'drive'
description: 'drive'
parameter: 'drive'
alias: 'd'
}
]
permission: 'user'
root: true
action: (params, options, done) ->
Promise = require('bluebird')
config = require('resin-config-json')
visuals = require('resin-cli-visuals')
umountAsync = Promise.promisify(require('umount').umount)
readFileAsync = Promise.promisify(require('fs').readFile)
Promise.try ->
return options.drive or visuals.drive('Select the device drive')
.tap(umountAsync)
.then (drive) ->
readFileAsync(params.file, 'utf8').then(JSON.parse).then (configJSON) ->
return config.write(drive, options.type, configJSON)
.tap ->
console.info('Done')
.nodeify(done)
exports.reconfigure =
signature: 'config reconfigure'
description: 'reconfigure a provisioned device'
help: '''
Use this command to reconfigure a provisioned device
Examples:
$ resin config reconfigure --type raspberry-pi
$ resin config reconfigure --type raspberry-pi --advanced
$ resin config reconfigure --type raspberry-pi --drive /dev/disk2
'''
options: [
{
signature: 'type'
description: 'device type (Check available types with `resin devices supported`)'
parameter: 'type'
alias: 't'
required: 'You have to specify a device type'
}
{
signature: 'drive'
description: 'drive'
parameter: 'drive'
alias: 'd'
}
{
signature: 'advanced'
description: 'show advanced commands'
boolean: true
alias: 'v'
}
]
permission: 'user'
root: true
action: (params, options, done) ->
Promise = require('bluebird')
config = require('resin-config-json')
visuals = require('resin-cli-visuals')
{ runCommand } = require('../utils/helpers')
umountAsync = Promise.promisify(require('umount').umount)
Promise.try ->
return options.drive or visuals.drive('Select the device drive')
.tap(umountAsync)
.then (drive) ->
config.read(drive, options.type).get('uuid')
.tap ->
umountAsync(drive)
.then (uuid) ->
configureCommand = "os configure #{drive} --device #{uuid}"
if options.advanced
configureCommand += ' --advanced'
return runCommand(configureCommand)
.then ->
console.info('Done')
.nodeify(done)
exports.generate =
signature: 'config generate'
description: 'generate a config.json file'
help: '''
Use this command to generate a config.json for a device or application.
Calling this command without --version is not recommended, and may fail in
future releases if the OS version cannot be inferred.
This is interactive by default, but you can do this automatically without interactivity
by specifying an option for each question on the command line, if you know the questions
that will be asked for the relevant device type.
Examples:
$ resin config generate --device 7cf02a6 --version 2.12.7
$ resin config generate --device 7cf02a6 --version 2.12.7 --generate-device-api-key
$ resin config generate --device 7cf02a6 --version 2.12.7 --device-api-key <existingDeviceKey>
$ resin config generate --device 7cf02a6 --version 2.12.7 --output config.json
$ resin config generate --app MyApp --version 2.12.7
$ resin config generate --app MyApp --version 2.12.7 --output config.json
$ resin config generate --app MyApp --version 2.12.7 \
--network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 1
'''
options: [
commandOptions.optionalOsVersion
commandOptions.optionalApplication
commandOptions.optionalDevice
commandOptions.optionalDeviceApiKey
{
signature: 'generate-device-api-key'
description: 'generate a fresh device key for the device'
boolean: true
}
{
signature: 'output'
description: 'output'
parameter: 'output'
alias: 'o'
}
# Options for non-interactive configuration
{
signature: 'network'
description: 'the network type to use: ethernet or wifi'
parameter: 'network'
}
{
signature: 'wifiSsid'
description: 'the wifi ssid to use (used only if --network is set to wifi)'
parameter: 'wifiSsid'
}
{
signature: 'wifiKey'
description: 'the wifi key to use (used only if --network is set to wifi)'
parameter: 'wifiKey'
}
{
signature: 'appUpdatePollInterval'
description: 'how frequently (in minutes) to poll for application updates'
parameter: 'appUpdatePollInterval'
}
]
permission: 'user'
action: (params, options, done) ->
normalizeUuidProp(options, 'device')
Promise = require('bluebird')
writeFileAsync = Promise.promisify(require('fs').writeFile)
resin = require('resin-sdk').fromSharedOptions()
form = require('resin-cli-form')
deviceConfig = require('resin-device-config')
prettyjson = require('prettyjson')
{ generateDeviceConfig, generateApplicationConfig } = require('../utils/config')
{ exitWithExpectedError } = require('../utils/patterns')
if not options.device? and not options.application?
exitWithExpectedError '''
You have to pass either a device or an application.
See the help page for examples:
$ resin help config generate
'''
Promise.try ->
if options.device?
return resin.models.device.get(options.device)
return resin.models.application.get(options.application)
.then (resource) ->
resin.models.device.getManifestBySlug(resource.device_type)
.get('options')
.then (formOptions) ->
# Pass params as an override: if there is any param with exactly the same name as a
# required option, that value is used (and the corresponding question is not asked)
form.run(formOptions, override: options)
.then (answers) ->
answers.version = options.version
if resource.uuid?
generateDeviceConfig(resource, options.deviceApiKey || options['generate-device-api-key'], answers)
else
generateApplicationConfig(resource, answers)
.then (config) ->
deviceConfig.validate(config)
if options.output?
return writeFileAsync(options.output, JSON.stringify(config))
console.log(prettyjson.render(config))
.nodeify(done)

220
lib/actions/deploy.coffee Normal file
View File

@ -0,0 +1,220 @@
# Imported here because it's needed for the setup
# of this action
Promise = require('bluebird')
dockerUtils = require('../utils/docker')
compose = require('../utils/compose')
###
Opts must be an object with the following keys:
app: the application instance to deploy to
image: the image to deploy; optional
shouldPerformBuild
shouldUploadLogs
buildEmulated
buildOpts: arguments to forward to docker build command
###
deployProject = (docker, logger, composeOpts, opts) ->
_ = require('lodash')
doodles = require('resin-doodles')
sdk = require('resin-sdk').fromSharedOptions()
compose.loadProject(
logger
composeOpts.projectPath
composeOpts.projectName
opts.image
)
.then (project) ->
if project.descriptors.length > 1 and !opts.app.application_type?[0]?.supports_multicontainer
throw new Error('Target application does not support multiple containers. Aborting!')
# find which services use images that already exist locally
Promise.map project.descriptors, (d) ->
# unconditionally build (or pull) if explicitly requested
return d if opts.shouldPerformBuild
docker.getImage(d.image.tag ? d.image).inspect()
.return(d.serviceName)
.catchReturn()
.filter (d) -> !!d
.then (servicesToSkip) ->
# multibuild takes in a composition and always attempts to
# build or pull all services. we workaround that here by
# passing a modified composition.
compositionToBuild = _.cloneDeep(project.composition)
compositionToBuild.services = _.omit(compositionToBuild.services, servicesToSkip)
if _.size(compositionToBuild.services) is 0
logger.logInfo('Everything is up to date (use --build to force a rebuild)')
return {}
compose.buildProject(
docker
logger
project.path
project.name
compositionToBuild
opts.app.arch
opts.app.device_type
opts.buildEmulated
opts.buildOpts
composeOpts.inlineLogs
)
.then (builtImages) ->
_.keyBy(builtImages, 'serviceName')
.then (builtImages) ->
project.descriptors.map (d) ->
builtImages[d.serviceName] ? {
serviceName: d.serviceName,
name: d.image.tag ? d.image
logs: 'Build skipped; image for service already exists.'
props: {}
}
.then (images) ->
if opts.app.application_type?[0]?.is_legacy
chalk = require('chalk')
legacyDeploy = require('../utils/deploy-legacy')
msg = chalk.yellow('Target application requires legacy deploy method.')
logger.logWarn(msg)
return Promise.join(
docker
logger
sdk.auth.getToken()
sdk.auth.whoami()
sdk.settings.get('resinUrl')
{
appName: opts.app.app_name
imageName: images[0].name
buildLogs: images[0].logs
shouldUploadLogs: opts.shouldUploadLogs
}
legacyDeploy
)
.then (releaseId) ->
sdk.models.release.get(releaseId, $select: [ 'commit' ])
Promise.join(
sdk.auth.getUserId()
sdk.auth.getToken()
sdk.settings.get('apiUrl')
(userId, auth, apiEndpoint) ->
compose.deployProject(
docker
logger
project.composition
images
opts.app.id
userId
"Bearer #{auth}"
apiEndpoint
!opts.shouldUploadLogs
)
)
.then (release) ->
logger.logSuccess('Deploy succeeded!')
logger.logSuccess("Release: #{release.commit}")
console.log()
console.log(doodles.getDoodle()) # Show charlie
console.log()
.tapCatch (e) ->
logger.logError('Deploy failed')
module.exports =
signature: 'deploy <appName> [image]'
description: 'Deploy a single image or a multicontainer project to a resin.io application'
help: '''
Use this command to deploy an image or a complete multicontainer project
to an application, optionally building it first.
Usage: `deploy <appName> ([image] | --build [--source build-dir])`
Unless an image is specified, this command will look into the current directory
(or the one specified by --source) for a compose file. If one is found, this
command will deploy each service defined in the compose file, building it first
if an image for it doesn't exist. If a compose file isn't found, the command
will look for a Dockerfile, and if yet that isn't found, it will try to
generate one.
To deploy to an app on which you're a collaborator, use
`resin deploy <appOwnerUsername>/<appName>`.
Note: If building with this command, all options supported by `resin build`
are also supported with this command.
Examples:
$ resin deploy myApp
$ resin deploy myApp --build --source myBuildDir/
$ resin deploy myApp myApp/myImage
'''
permission: 'user'
primary: true
options: dockerUtils.appendOptions compose.appendOptions [
{
signature: 'source'
parameter: 'source'
description: 'Specify an alternate source directory; default is the working directory'
alias: 's'
},
{
signature: 'build'
boolean: true
description: 'Force a rebuild before deploy'
alias: 'b'
},
{
signature: 'nologupload'
description: "Don't upload build logs to the dashboard with image (if building)"
boolean: true
}
]
action: (params, options, done) ->
# compositions with many services trigger misleading warnings
require('events').defaultMaxListeners = 1000
helpers = require('../utils/helpers')
Logger = require('../utils/logger')
logger = new Logger()
logger.logDebug('Parsing input...')
Promise.try ->
{ appName, image } = params
# look into "resin build" options if appName isn't given
appName = options.application if not appName?
delete options.application
if not appName?
throw new Error('Please specify the name of the application to deploy')
if image? and options.build
throw new Error('Build option is not applicable when specifying an image')
Promise.join(
helpers.getApplication(appName)
helpers.getArchAndDeviceType(appName)
(app, { arch, device_type }) ->
app.arch = arch
app.device_type = device_type
return app
)
.then (app) ->
[ app, image, !!options.build, !options.nologupload ]
.then ([ app, image, shouldPerformBuild, shouldUploadLogs ]) ->
Promise.join(
dockerUtils.getDocker(options)
dockerUtils.generateBuildOpts(options)
compose.generateOpts(options)
(docker, buildOpts, composeOpts) ->
deployProject(docker, logger, composeOpts, {
app
image
shouldPerformBuild
shouldUploadLogs
buildEmulated: !!options.emulated
buildOpts
})
)
.asCallback(done)

View File

@ -1,134 +1,119 @@
_ = require('lodash-contrib')
path = require('path')
async = require('async')
resin = require('resin-sdk')
visuals = require('resin-cli-visuals')
vcs = require('resin-vcs')
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
commandOptions = require('./command-options')
osAction = require('./os')
_ = require('lodash')
{ normalizeUuidProp } = require('../utils/normalization')
expandForAppName = {
$expand: belongs_to__application: $select: 'app_name'
}
exports.list =
signature: 'devices'
description: 'list all devices'
help: '''
Use this command to list all devices that belong to a certain application.
Use this command to list all devices that belong to you.
You can filter the devices by application by using the `--application` option.
Examples:
$ resin devices
$ resin devices --application MyApp
$ resin devices --app MyApp
$ resin devices -a MyApp
'''
options: [ commandOptions.application ]
options: [ commandOptions.optionalApplication ]
permission: 'user'
primary: true
action: (params, options, done) ->
resin.models.device.getAllByApplication options.application, (error, devices) ->
return done(error) if error?
console.log visuals.widgets.table.horizontal devices, [
Promise = require('bluebird')
resin = require('resin-sdk').fromSharedOptions()
visuals = require('resin-cli-visuals')
Promise.try ->
if options.application?
return resin.models.device.getAllByApplication(options.application, expandForAppName)
return resin.models.device.getAll(expandForAppName)
.tap (devices) ->
devices = _.map devices, (device) ->
device.dashboard_url = resin.models.device.getDashboardUrl(device.uuid)
device.application_name = device.belongs_to__application[0].app_name
device.uuid = device.uuid.slice(0, 7)
return device
console.log visuals.table.horizontal devices, [
'id'
'name'
'uuid'
'device_name'
'device_type'
'is_online'
'application_name'
'status'
'last_seen'
'is_online'
'supervisor_version'
'os_version'
'dashboard_url'
]
return done()
.nodeify(done)
exports.info =
signature: 'device <name>'
signature: 'device <uuid>'
description: 'list a single device'
help: '''
Use this command to show information about a single device.
Examples:
$ resin device MyDevice
$ resin device 7cf02a6
'''
permission: 'user'
primary: true
action: (params, options, done) ->
resin.models.device.get params.name, (error, device) ->
return done(error) if error?
console.log visuals.widgets.table.vertical device, [
'id'
'name'
'device_type'
'is_online'
'ip_address'
'application_name'
'status'
'last_seen'
'uuid'
'commit'
'supervisor_version'
'is_web_accessible'
'note'
]
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
visuals = require('resin-cli-visuals')
return done()
resin.models.device.get(params.uuid, expandForAppName)
.then (device) ->
resin.models.device.getStatus(device).then (status) ->
device.status = status
device.dashboard_url = resin.models.device.getDashboardUrl(device.uuid)
device.application_name = device.belongs_to__application[0].app_name
device.commit = device.is_on__commit
exports.remove =
signature: 'device rm <name>'
description: 'remove a device'
help: '''
Use this command to remove a device from resin.io.
Notice this command asks for confirmation interactively.
You can avoid this by passing the `--yes` boolean option.
Examples:
$ resin device rm MyDevice
$ resin device rm MyDevice --yes
'''
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
visuals.patterns.remove 'device', options.yes, (callback) ->
resin.models.device.remove(params.name, callback)
, done
exports.identify =
signature: 'device identify <uuid>'
description: 'identify a device with a UUID'
help: '''
Use this command to identify a device.
In the Raspberry Pi, the ACT led is blinked several times.
Examples:
$ resin device identify 23c73a12e3527df55c60b9ce647640c1b7da1b32d71e6a39849ac0f00db828
'''
permission: 'user'
action: (params, options, done) ->
resin.models.device.identify(params.uuid, done)
exports.rename =
signature: 'device rename <name> [newName]'
description: 'rename a resin device'
help: '''
Use this command to rename a device.
If you omit the name, you'll get asked for it interactively.
Examples:
$ resin device rename MyDevice MyPi
$ resin device rename MyDevice
'''
permission: 'user'
action: (params, options, done) ->
async.waterfall [
(callback) ->
if not _.isEmpty(params.newName)
return callback(null, params.newName)
visuals.widgets.ask('How do you want to name this device?', null, callback)
(newName, callback) ->
resin.models.device.rename(params.name, newName, callback)
], done
console.log visuals.table.vertical device, [
"$#{device.device_name}$"
'id'
'device_type'
'status'
'is_online'
'ip_address'
'application_name'
'last_seen'
'uuid'
'commit'
'supervisor_version'
'is_web_accessible'
'note'
'os_version'
'dashboard_url'
]
.nodeify(done)
exports.supported =
signature: 'devices supported'
@ -140,82 +125,333 @@ exports.supported =
$ resin devices supported
'''
permission: 'user'
action: (params, options, done) ->
resin.models.device.getSupportedDeviceTypes (error, devices) ->
return done(error) if error?
_.each(devices, _.unary(console.log))
done()
resin = require('resin-sdk').fromSharedOptions()
visuals = require('resin-cli-visuals')
exports.init =
signature: 'device init [device]'
description: 'initialise a device with resin os'
resin.models.config.getDeviceTypes().then (deviceTypes) ->
console.log visuals.table.horizontal deviceTypes, [
'slug'
'name'
]
.nodeify(done)
exports.register =
signature: 'device register <application>'
description: 'register a device'
help: '''
Use this command to download the OS image of a certain application and write it to an SD Card.
Use this command to register a device to an application.
Note that this command requires admin privileges.
Examples:
If `device` is omitted, you will be prompted to select a device interactively.
$ resin device register MyApp
$ resin device register MyApp --uuid <uuid>
'''
permission: 'user'
options: [
{
signature: 'uuid'
description: 'custom uuid'
parameter: 'uuid'
alias: 'u'
}
]
action: (params, options, done) ->
Promise = require('bluebird')
resin = require('resin-sdk').fromSharedOptions()
Promise.join(
resin.models.application.get(params.application)
options.uuid ? resin.models.device.generateUniqueKey()
(application, uuid) ->
console.info("Registering to #{application.app_name}: #{uuid}")
return resin.models.device.register(application.id, uuid)
)
.get('uuid')
.nodeify(done)
exports.remove =
signature: 'device rm <uuid>'
description: 'remove a device'
help: '''
Use this command to remove a device from resin.io.
Notice this command asks for confirmation interactively.
You can avoid this by passing the `--yes` boolean option.
You can quiet the progress bar by passing the `--quiet` boolean option.
Examples:
You may have to unmount the device before attempting this operation.
$ resin device rm 7cf02a6
$ resin device rm 7cf02a6 --yes
'''
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
patterns = require('../utils/patterns')
You need to configure the network type and other settings:
patterns.confirm(options.yes, 'Are you sure you want to delete the device?').then ->
resin.models.device.remove(params.uuid)
.nodeify(done)
Ethernet:
You can setup the device OS to use ethernet by setting the `--network` option to "ethernet".
exports.identify =
signature: 'device identify <uuid>'
description: 'identify a device with a UUID'
help: '''
Use this command to identify a device.
Wifi:
You can setup the device OS to use wifi by setting the `--network` option to "wifi".
If you set "network" to "wifi", you will need to specify the `--ssid` and `--key` option as well.
In the Raspberry Pi, the ACT led is blinked several times.
You can omit network related options to be asked about them interactively.
Examples:
$ resin device identify 23c73a1
'''
permission: 'user'
action: (params, options, done) ->
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
resin.models.device.identify(params.uuid).nodeify(done)
exports.reboot =
signature: 'device reboot <uuid>'
description: 'restart a device'
help: '''
Use this command to remotely reboot a device
Examples:
$ resin device reboot 23c73a1
'''
options: [ commandOptions.forceUpdateLock ]
permission: 'user'
action: (params, options, done) ->
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
resin.models.device.reboot(params.uuid, options).nodeify(done)
exports.shutdown =
signature: 'device shutdown <uuid>'
description: 'shutdown a device'
help: '''
Use this command to remotely shutdown a device
Examples:
$ resin device shutdown 23c73a1
'''
options: [ commandOptions.forceUpdateLock ]
permission: 'user'
action: (params, options, done) ->
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
resin.models.device.shutdown(params.uuid, options).nodeify(done)
exports.enableDeviceUrl =
signature: 'device public-url enable <uuid>'
description: 'enable public URL for a device'
help: '''
Use this command to enable public URL for a device
Examples:
$ resin device public-url enable 23c73a1
'''
permission: 'user'
action: (params, options, done) ->
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
resin.models.device.enableDeviceUrl(params.uuid).nodeify(done)
exports.disableDeviceUrl =
signature: 'device public-url disable <uuid>'
description: 'disable public URL for a device'
help: '''
Use this command to disable public URL for a device
Examples:
$ resin device public-url disable 23c73a1
'''
permission: 'user'
action: (params, options, done) ->
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
resin.models.device.disableDeviceUrl(params.uuid).nodeify(done)
exports.getDeviceUrl =
signature: 'device public-url <uuid>'
description: 'gets the public URL of a device'
help: '''
Use this command to get the public URL of a device
Examples:
$ resin device public-url 23c73a1
'''
permission: 'user'
action: (params, options, done) ->
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
resin.models.device.getDeviceUrl(params.uuid).then (url) ->
console.log(url)
.nodeify(done)
exports.hasDeviceUrl =
signature: 'device public-url status <uuid>'
description: 'Returns true if public URL is enabled for a device'
help: '''
Use this command to determine if public URL is enabled for a device
Examples:
$ resin device public-url status 23c73a1
'''
permission: 'user'
action: (params, options, done) ->
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
resin.models.device.hasDeviceUrl(params.uuid).then (hasDeviceUrl) ->
console.log(hasDeviceUrl)
.nodeify(done)
exports.rename =
signature: 'device rename <uuid> [newName]'
description: 'rename a resin device'
help: '''
Use this command to rename a device.
If you omit the name, you'll get asked for it interactively.
Examples:
$ resin device rename 7cf02a6
$ resin device rename 7cf02a6 MyPi
'''
permission: 'user'
action: (params, options, done) ->
normalizeUuidProp(params)
Promise = require('bluebird')
resin = require('resin-sdk').fromSharedOptions()
form = require('resin-cli-form')
Promise.try ->
return params.newName if not _.isEmpty(params.newName)
form.ask
message: 'How do you want to name this device?'
type: 'input'
.then(_.partial(resin.models.device.rename, params.uuid))
.nodeify(done)
exports.move =
signature: 'device move <uuid>'
description: 'move a device to another application'
help: '''
Use this command to move a device to another application you own.
If you omit the application, you'll get asked for it interactively.
Examples:
$ resin device move 7cf02a6
$ resin device move 7cf02a6 --application MyNewApp
'''
permission: 'user'
options: [ commandOptions.optionalApplication ]
action: (params, options, done) ->
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
patterns = require('../utils/patterns')
resin.models.device.get(params.uuid, expandForAppName).then (device) ->
return options.application or patterns.selectApplication (application) ->
return _.every [
application.device_type is device.device_type
device.belongs_to__application[0].app_name isnt application.app_name
]
.tap (application) ->
return resin.models.device.move(params.uuid, application)
.then (application) ->
console.info("#{params.uuid} was moved to #{application}")
.nodeify(done)
exports.init =
signature: 'device init'
description: 'initialise a device with resinOS'
help: '''
Use this command to download the OS image of a certain application and write it to an SD Card.
Notice this command may ask for confirmation interactively.
You can avoid this by passing the `--yes` boolean option.
Examples:
$ resin device init
$ resin device init --application 91
$ resin device init --application 91 --network ethernet
$ resin device init /dev/disk2 --application 91 --network wifi --ssid MyNetwork --key secret
$ resin device init --application MyApp
'''
options: [
commandOptions.optionalApplication
commandOptions.network
commandOptions.wifiSsid
commandOptions.wifiKey
commandOptions.yes
commandOptions.advancedConfig
_.assign({}, commandOptions.osVersion, { signature: 'os-version', parameter: 'os-version' })
commandOptions.drive
{
signature: 'config'
description: 'path to the config JSON file, see `resin os build-config`'
parameter: 'config'
}
]
permission: 'user'
action: (params, options, done) ->
Promise = require('bluebird')
rimraf = Promise.promisify(require('rimraf'))
tmp = require('tmp')
tmpNameAsync = Promise.promisify(tmp.tmpName)
tmp.setGracefulCleanup()
params.id = options.application
resin = require('resin-sdk').fromSharedOptions()
patterns = require('../utils/patterns')
{ runCommand } = require('../utils/helpers')
async.waterfall([
Promise.try ->
return options.application if options.application?
return patterns.selectApplication()
.then(resin.models.application.get)
.then (application) ->
(callback) ->
return callback(null, options.application) if options.application?
vcs.getApplicationId(process.cwd(), callback)
download = ->
tmpNameAsync().then (tempPath) ->
osVersion = options['os-version'] or 'default'
runCommand("os download #{application.device_type} --output '#{tempPath}' --version #{osVersion}")
.disposer (tempPath) ->
return rimraf(tempPath)
(applicationId, callback) ->
params.id = applicationId
return callback(null, params.device) if params.device?
visuals.patterns.selectDrive(callback)
Promise.using download(), (tempPath) ->
runCommand("device register #{application.app_name}")
.then(resin.models.device.get)
.tap (device) ->
configureCommand = "os configure '#{tempPath}' --device #{device.uuid}"
if options.config
configureCommand += " --config '#{options.config}'"
else if options.advanced
configureCommand += ' --advanced'
runCommand(configureCommand)
.then ->
osInitCommand = "os initialize '#{tempPath}' --type #{application.device_type}"
if options.yes
osInitCommand += ' --yes'
if options.drive
osInitCommand += " --drive #{options.drive}"
runCommand(osInitCommand)
# Make sure the device resource is removed if there is an
# error when configuring or initializing a device image
.catch (error) ->
resin.models.device.remove(device.uuid).finally ->
throw error
.then (device) ->
console.log('Done')
return device.uuid
(device, callback) ->
params.device = device
message = "This will completely erase #{params.device}. Are you sure you want to continue?"
visuals.patterns.confirm(options.yes, message, callback)
(confirmed, callback) ->
return done() if not confirmed
options.yes = confirmed
osAction.download.action(params, options, callback)
(outputFile, callback) ->
params.image = outputFile
osAction.install.action(params, options, callback)
], done)
.nodeify(done)

View File

@ -1,32 +0,0 @@
_ = require('lodash')
async = require('async')
visuals = require('resin-cli-visuals')
drivelist = require('drivelist')
exports.list =
signature: 'drives'
description: 'list available drives'
help: '''
Use this command to list all drives that are connected to your machine.
Examples:
$ resin drives
'''
permission: 'user'
action: (params, options, done) ->
drivelist.list (error, drives) ->
return done(error) if error?
async.reject drives, drivelist.isSystem, (removableDrives) ->
if _.isEmpty(removableDrives)
return done(new Error('No removable devices available'))
console.log visuals.widgets.table.horizontal removableDrives, [
'device'
'description'
'size'
]
return done()

View File

@ -1,26 +1,46 @@
_ = require('lodash-contrib')
resin = require('resin-sdk')
visuals = require('resin-cli-visuals')
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
commandOptions = require('./command-options')
{ normalizeUuidProp } = require('../utils/normalization')
exports.list =
signature: 'envs'
description: 'list all environment variables'
help: '''
Use this command to list all environment variables for a particular application.
Notice we will support per-device environment variables soon.
Use this command to list all environment variables for
a particular application or device.
This command lists all custom environment variables set on the devices running
the application. If you want to see all environment variables, including private
This command lists all custom environment variables.
If you want to see all environment variables, including private
ones used by resin, use the verbose option.
At the moment the CLI doesn't fully support multi-container applications,
so the following commands will only show service variables,
without showing which service they belong to.
Example:
$ resin envs --application 91
$ resin envs --application 91 --verbose
$ resin envs --application MyApp
$ resin envs --application MyApp --verbose
$ resin envs --device 7cf02a6
'''
options: [
commandOptions.application
commandOptions.optionalApplication
commandOptions.optionalDevice
{
signature: 'verbose'
@ -31,19 +51,35 @@ exports.list =
]
permission: 'user'
action: (params, options, done) ->
resin.models.environmentVariables.getAllByApplication options.application, (error, environmentVariables) ->
return done(error) if error?
normalizeUuidProp(options, 'device')
Promise = require('bluebird')
_ = require('lodash')
resin = require('resin-sdk-preconfigured')
visuals = require('resin-cli-visuals')
{ exitWithExpectedError } = require('../utils/patterns')
Promise.try ->
if options.application?
return resin.models.environmentVariables.getAllByApplication(options.application)
else if options.device?
return resin.models.environmentVariables.device.getAll(options.device)
else
exitWithExpectedError('You must specify an application or device')
.tap (environmentVariables) ->
if _.isEmpty(environmentVariables)
exitWithExpectedError('No environment variables found')
if not options.verbose
environmentVariables = _.reject(environmentVariables, resin.models.environmentVariables.isSystemVariable)
isSystemVariable = resin.models.environmentVariables.isSystemVariable
environmentVariables = _.reject(environmentVariables, isSystemVariable)
console.log visuals.widgets.table.horizontal environmentVariables, [
console.log visuals.table.horizontal environmentVariables, [
'id'
'name'
'value'
]
return done()
.nodeify(done)
exports.remove =
signature: 'env rm <id>'
@ -56,17 +92,29 @@ exports.remove =
Notice this command asks for confirmation interactively.
You can avoid this by passing the `--yes` boolean option.
If you want to eliminate a device environment variable, pass the `--device` boolean option.
Examples:
$ resin env rm 215
$ resin env rm 215 --yes
$ resin env rm 215 --device
'''
options: [ commandOptions.yes ]
options: [
commandOptions.yes
commandOptions.booleanDevice
]
permission: 'user'
action: (params, options, done) ->
visuals.patterns.remove 'environment variable', options.yes, (callback) ->
resin.models.environmentVariables.remove(params.id, callback)
, done
resin = require('resin-sdk-preconfigured')
patterns = require('../utils/patterns')
patterns.confirm(options.yes, 'Are you sure you want to delete the environment variable?').then ->
if options.device
resin.models.environmentVariables.device.remove(params.id)
else
resin.models.environmentVariables.remove(params.id)
.nodeify(done)
exports.add =
signature: 'env add <key> [value]'
@ -74,31 +122,53 @@ exports.add =
help: '''
Use this command to add an enviroment variable to an application.
You need to pass the `--application` option.
At the moment the CLI doesn't fully support multi-container applications,
so the following commands will only set service variables for the first
service in your application.
If value is omitted, the tool will attempt to use the variable's value
as defined in your host machine.
Use the `--device` option if you want to assign the environment variable
to a specific device.
If the value is grabbed from the environment, a warning message will be printed.
Use `--quiet` to remove it.
Examples:
$ resin env add EDITOR vim -a 91
$ resin env add TERM -a 91
$ resin env add EDITOR vim --application MyApp
$ resin env add TERM --application MyApp
$ resin env add EDITOR vim --device 7cf02a6
'''
options: [ commandOptions.application ]
options: [
commandOptions.optionalApplication
commandOptions.optionalDevice
]
permission: 'user'
action: (params, options, done) ->
if not params.value?
params.value = process.env[params.key]
normalizeUuidProp(options, 'device')
Promise = require('bluebird')
resin = require('resin-sdk-preconfigured')
{ exitWithExpectedError } = require('../utils/patterns')
Promise.try ->
if not params.value?
return done(new Error("Environment value not found for key: #{params.key}"))
else
console.info("Warning: using #{params.key}=#{params.value} from host environment")
params.value = process.env[params.key]
resin.models.environmentVariables.create(options.application, params.key, params.value, done)
if not params.value?
throw new Error("Environment value not found for key: #{params.key}")
else
console.info("Warning: using #{params.key}=#{params.value} from host environment")
if options.application?
resin.models.environmentVariables.create(options.application, params.key, params.value)
else if options.device?
resin.models.environmentVariables.device.create(options.device, params.key, params.value)
else
exitWithExpectedError('You must specify an application or device')
.nodeify(done)
exports.rename =
signature: 'env rename <id> <value>'
@ -106,10 +176,22 @@ exports.rename =
help: '''
Use this command to rename an enviroment variable from an application.
Pass the `--device` boolean option if you want to rename a device environment variable.
Examples:
$ resin env rename 376 emacs
$ resin env rename 376 emacs --device
'''
permission: 'user'
options: [ commandOptions.booleanDevice ]
action: (params, options, done) ->
resin.models.environmentVariables.update(params.id, params.value, done)
Promise = require('bluebird')
resin = require('resin-sdk-preconfigured')
Promise.try ->
if options.device
resin.models.environmentVariables.device.update(params.id, params.value)
else
resin.models.environmentVariables.update(params.id, params.value)
.nodeify(done)

View File

@ -1,90 +0,0 @@
async = require('async')
fs = require('fs')
path = require('path')
_ = require('lodash')
resin = require('resin-sdk')
visuals = require('resin-cli-visuals')
vcs = require('resin-vcs')
examplesData = require('../data/examples.json')
exports.list =
signature: 'examples'
description: 'list all example applications'
help: '''
Use this command to list available example applications from resin.io
Example:
$ resin examples
'''
permission: 'user'
action: ->
examplesData = _.map examplesData, (example, index) ->
example.id = index + 1
return example
examplesData = _.map examplesData, (example) ->
example.author ?= 'Unknown'
return example
console.log visuals.widgets.table.horizontal examplesData, [
'id'
'display_name'
'repository'
'author'
]
exports.info =
signature: 'example <id>'
description: 'list a single example application'
help: '''
Use this command to show information of a single example application
Example:
$ resin example 3
'''
permission: 'user'
action: (params, options, done) ->
id = params.id - 1
example = examplesData[id]
if not example?
return done(new Error("Unknown example: #{id}"))
example.id = id
example.author ?= 'Unknown'
console.log visuals.widgets.table.vertical example, [
'id'
'display_name'
'description'
'author'
'repository'
]
return done()
exports.clone =
signature: 'example clone <id>'
description: 'clone an example application'
help: '''
Use this command to clone an example application to the current directory
This command outputs information about the cloning process.
Use `--quiet` to remove that output.
Example:
$ resin example clone 3
'''
permission: 'user'
action: (params, options, done) ->
example = examplesData[params.id - 1]
if not example?
return done(new Error("Unknown example: #{id}"))
currentDirectory = process.cwd()
console.info("Cloning #{example.display_name} to #{currentDirectory}")
vcs.clone(example.repository, currentDirectory, done)

View File

@ -1,115 +1,96 @@
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
_ = require('lodash')
_.str = require('underscore.string')
resin = require('resin-sdk')
capitano = require('capitano')
columnify = require('columnify')
messages = require('../utils/messages')
{ exitWithExpectedError } = require('../utils/patterns')
# TODO: Refactor this terrible mess
parse = (object) ->
return _.fromPairs _.map object, (item) ->
PADDING_INITIAL = ' '
PADDING_MIDDLE = '\t'
# Hacky way to determine if an object is
# a function or a command
if item.alias?
signature = item.toString()
else
signature = item.signature.toString()
getFieldMaxLength = (array, field) ->
return _.max _.map array, (item) ->
return item[field].toString().length
return [
signature
item.description
]
buildHelpString = (firstColumn, secondColumn) ->
result = "#{PADDING_INITIAL}#{firstColumn}"
result += "#{PADDING_MIDDLE}#{secondColumn}"
return result
indent = (text) ->
text = _.map text.split('\n'), (line) ->
return ' ' + line
return text.join('\n')
addOptionPrefix = (option) ->
return if option.length <= 0
if option.length is 1
return "-#{option}"
else
return "--#{option}"
print = (data) ->
console.log indent columnify data,
showHeaders: false
minWidth: 35
addAlias = (alias) ->
return ", #{addOptionPrefix(alias)}"
buildOptionSignatureHelp = (option) ->
result = addOptionPrefix(option.signature.toString())
if _.isString(option.alias)
result += addAlias(option.alias)
else if _.isArray(option.alias)
for alias in option.alias
result += addAlias(alias)
if option.parameter?
result += " <#{option.parameter}>"
return result
getCommandHelp = (command) ->
maxSignatureLength = getFieldMaxLength(capitano.state.commands, 'signature')
commandSignature = _.str.rpad(command.signature.toString(), maxSignatureLength, ' ')
return buildHelpString(commandSignature, command.description)
getOptionsParsedSignatures = (optionsHelp) ->
maxLength = _.max _.map optionsHelp, (signature) ->
return signature.length
return _.map optionsHelp, (signature) ->
return _.str.rpad(signature, maxLength, ' ')
getOptionHelp = (option, maxLength) ->
result = PADDING_INITIAL
result += _.str.rpad(option.signature, maxLength, ' ')
result += PADDING_MIDDLE
result += option.description
return result
general = ->
general = (params, options, done) ->
console.log('Usage: resin [COMMAND] [OPTIONS]\n')
console.log('Commands:\n')
console.log(messages.reachingOut)
console.log('\nPrimary commands:\n')
for command in capitano.state.commands
continue if command.isWildcard()
console.log(getCommandHelp(command))
# We do not want the wildcard command
# to be printed in the help screen.
commands = _.reject capitano.state.commands, (command) ->
return command.hidden or command.isWildcard()
console.log('\nGlobal Options:\n')
groupedCommands = _.groupBy commands, (command) ->
if command.primary
return 'primary'
return 'secondary'
options = _.map capitano.state.globalOptions, (option) ->
option.signature = buildOptionSignatureHelp(option)
return option
print(parse(groupedCommands.primary))
optionSignatureMaxLength = _.max _.map options, (option) ->
return option.signature.length
if options.verbose
console.log('\nAdditional commands:\n')
print(parse(groupedCommands.secondary))
else
console.log('\nRun `resin help --verbose` to list additional commands')
for option in options
console.log(getOptionHelp(option, optionSignatureMaxLength))
if not _.isEmpty(capitano.state.globalOptions)
console.log('\nGlobal Options:\n')
print(parse(capitano.state.globalOptions))
console.log()
return done()
command = (params, options, done) ->
capitano.state.getMatchCommand params.command, (error, command) ->
return done(error) if error?
if not command? or command.isWildcard()
return capitano.defaults.actions.commandNotFound(params.command)
exitWithExpectedError("Command not found: #{params.command}")
console.log("Usage: #{command.signature}")
if command.help?
console.log("\n#{command.help}")
else if command.description?
console.log("\n#{_.str.humanize(command.description)}")
console.log("\n#{_.capitalize(command.description)}")
if not _.isEmpty(command.options)
console.log('\nOptions:\n')
options = _.map command.options, (option) ->
option.signature = buildOptionSignatureHelp(option)
return option
optionSignatureMaxLength = _.max _.map options, (option) ->
return option.signature.toString().length
for option in options
console.log(getOptionHelp(option, optionSignatureMaxLength))
console.log()
print(parse(command.options))
return done()
@ -124,6 +105,13 @@ exports.help =
$ resin help apps
$ resin help os download
'''
primary: true
options: [
signature: 'verbose'
description: 'show additional commands'
boolean: true
alias: 'v'
]
action: (params, options, done) ->
if params.command?
command(params, options, done)

View File

@ -1,16 +1,42 @@
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
module.exports =
wizard: require('./wizard')
apiKey: require('./api-key')
app: require('./app')
info: require('./info')
auth: require('./auth')
drive: require('./drive')
info: require('./info')
device: require('./device')
env: require('./environment-variables')
keys: require('./keys')
logs: require('./logs')
local: require('./local')
notes: require('./notes')
preferences: require('./preferences')
os: require('./os')
help: require('./help')
examples: require('./examples')
plugin: require('./plugin')
update: require('./update')
os: require('./os')
settings: require('./settings')
config: require('./config')
sync: require('./sync')
ssh: require('./ssh')
internal: require('./internal')
build: require('./build')
deploy: require('./deploy')
util: require('./util')
preload: require('./preload')
push: require('./push')
join: require('./join')
leave: require('./leave')

View File

@ -1,10 +0,0 @@
packageJSON = require('../../package.json')
exports.version =
signature: 'version'
description: 'output the version number'
help: '''
Display the Resin CLI version.
'''
action: ->
console.log(packageJSON.version)

30
lib/actions/info.ts Normal file
View File

@ -0,0 +1,30 @@
/*
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { CommandDefinition } from 'capitano';
export const version: CommandDefinition = {
signature: 'version',
description: 'output the version number',
help: `\
Display the Resin CLI version.\
`,
async action(_params, _options, done) {
const packageJSON = await import('../../package.json');
console.log(packageJSON.version);
return done();
},
};

View File

@ -0,0 +1,87 @@
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
# These are internal commands we want to be runnable from the outside
# One use-case for this is spawning the minimal operation with root priviledges
exports.osInit =
signature: 'internal osinit <image> <type> <config>'
description: 'do actual init of the device with the preconfigured os image'
help: '''
Don't use this command directly! Use `resin os initialize <image>` instead.
'''
hidden: true
root: true
action: (params, options, done) ->
Promise = require('bluebird')
init = require('resin-device-init')
helpers = require('../utils/helpers')
return Promise.try ->
config = JSON.parse(params.config)
init.initialize(params.image, params.type, config)
.then(helpers.osProgressHandler)
.nodeify(done)
exports.scanDevices =
signature: 'internal scandevices'
description: 'scan for local resin-enabled devices and show a picker to choose one'
help: '''
Don't use this command directly!
'''
hidden: true
root: true
action: (params, options, done) ->
Promise = require('bluebird')
{ forms } = require('resin-sync')
return Promise.try ->
forms.selectLocalResinOsDevice()
.then (hostnameOrIp) ->
console.error("==> Selected device: #{hostnameOrIp}")
.nodeify(done)
exports.sudo =
signature: 'internal sudo <command>'
description: 'execute arbitrary commands in a privileged subprocess'
help: '''
Don't use this command directly!
<command> must be passed as a single argument. That means, you need to make sure
you enclose <command> in quotes (eg. resin internal sudo 'ls -alF') if for
whatever reason you invoke the command directly or, typically, pass <command>
as a single argument to spawn (eg. `spawn('resin', [ 'internal', 'sudo', 'ls -alF' ])`).
Furthermore, this command will naively split <command> on whitespace and directly
forward the parts as arguments to `sudo`, so be careful.
'''
hidden: true
action: (params, options, done) ->
os = require('os')
Promise = require('bluebird')
return Promise.try ->
if os.platform() is 'win32'
windosu = require('windosu')
windosu.exec(params.command, {})
else
{ spawn } = require('child_process')
{ wait } = require('rindle')
cmd = params.command.split(' ')
ps = spawn('sudo', cmd, stdio: 'inherit', env: process.env)
wait(ps)
.nodeify(done)

62
lib/actions/join.ts Normal file
View File

@ -0,0 +1,62 @@
/*
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as Bluebird from 'bluebird';
import { CommandDefinition } from 'capitano';
import { stripIndent } from 'common-tags';
interface Args {
deviceIp?: string;
}
interface Options {
application?: string;
}
export const join: CommandDefinition<Args, Options> = {
signature: 'join [deviceIp]',
description:
'Promote a local device running unmanaged resinOS to join a resin.io application',
help: stripIndent`
Examples:
$ resin join
$ resin join resin.local
$ resin join resin.local --application MyApp
$ resin join 192.168.1.25
$ resin join 192.168.1.25 --application MyApp
`,
options: [
{
signature: 'application',
parameter: 'application',
alias: 'a',
description: 'The name of the application the device should join',
},
],
primary: true,
async action(params, options, done) {
const resin = await import('resin-sdk');
const Logger = await import('../utils/logger');
const promote = await import('../utils/promote');
const sdk = resin.fromSharedOptions();
const logger = new Logger();
return Bluebird.try(() => {
return promote.join(logger, sdk, params.deviceIp, options.application);
}).nodeify(done);
},
};

View File

@ -1,10 +1,19 @@
_ = require('lodash')
_.str = require('underscore.string')
async = require('async')
fs = require('fs')
resin = require('resin-sdk')
capitano = require('capitano')
visuals = require('resin-cli-visuals')
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
commandOptions = require('./command-options')
exports.list =
@ -19,12 +28,15 @@ exports.list =
'''
permission: 'user'
action: (params, options, done) ->
resin.models.key.getAll (error, keys) ->
return done(error) if error?
console.log visuals.widgets.table.horizontal keys, [ 'id', 'title' ]
return done()
resin = require('resin-sdk').fromSharedOptions()
visuals = require('resin-cli-visuals')
SSH_KEY_WIDTH = 43
resin.models.key.getAll().then (keys) ->
console.log visuals.table.horizontal keys, [
'id'
'title'
]
.nodeify(done)
exports.info =
signature: 'key <id>'
@ -38,13 +50,20 @@ exports.info =
'''
permission: 'user'
action: (params, options, done) ->
resin.models.key.get params.id, (error, key) ->
return done(error) if error?
resin = require('resin-sdk').fromSharedOptions()
visuals = require('resin-cli-visuals')
key.public_key = '\n' + visuals.helpers.chop(key.public_key, SSH_KEY_WIDTH)
resin.models.key.get(params.id).then (key) ->
console.log visuals.table.vertical key, [
'id'
'title'
]
console.log(visuals.widgets.table.vertical(key, [ 'id', 'title', 'public_key' ]))
return done()
# Since the public key string is long, it might
# wrap to lines below, causing the table layout to break.
# See https://github.com/resin-io/resin-cli/issues/151
console.log('\n' + key.public_key)
.nodeify(done)
exports.remove =
signature: 'key rm <id>'
@ -63,9 +82,12 @@ exports.remove =
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
visuals.patterns.remove 'key', options.yes, (callback) ->
resin.models.key.remove(params.id, callback)
, done
resin = require('resin-sdk').fromSharedOptions()
patterns = require('../utils/patterns')
patterns.confirm(options.yes, 'Are you sure you want to delete the key?').then ->
resin.models.key.remove(params.id)
.nodeify(done)
exports.add =
signature: 'key add <name> [path]'
@ -83,16 +105,19 @@ exports.add =
'''
permission: 'user'
action: (params, options, done) ->
async.waterfall [
_ = require('lodash')
Promise = require('bluebird')
readFileAsync = Promise.promisify(require('fs').readFile)
capitano = require('capitano')
resin = require('resin-sdk').fromSharedOptions()
(callback) ->
if params.path?
fs.readFile(params.path, encoding: 'utf8', callback)
else
capitano.utils.getStdin (data) ->
return callback(null, data)
Promise.try ->
return readFileAsync(params.path, encoding: 'utf8') if params.path?
(key, callback) ->
resin.models.key.create(params.name, key, callback)
# TODO: should this be promisified for consistency?
Promise.fromNode (callback) ->
capitano.utils.getStdin (data) ->
return callback(null, data)
], done
.then(_.partial(resin.models.key.create, params.name))
.nodeify(done)

49
lib/actions/leave.ts Normal file
View File

@ -0,0 +1,49 @@
/*
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as Bluebird from 'bluebird';
import { CommandDefinition } from 'capitano';
import { stripIndent } from 'common-tags';
interface Args {
deviceIp?: string;
}
export const leave: CommandDefinition<Args, {}> = {
signature: 'leave [deviceIp]',
description: 'Detach a local device from its resin.io application',
help: stripIndent`
Examples:
$ resin leave
$ resin leave resin.local
$ resin leave 192.168.1.25
`,
options: [],
permission: 'user',
primary: true,
async action(params, _options, done) {
const resin = await import('resin-sdk');
const Logger = await import('../utils/logger');
const promote = await import('../utils/promote');
const sdk = resin.fromSharedOptions();
const logger = new Logger();
return Bluebird.try(() => {
return promote.leave(logger, sdk, params.deviceIp);
}).nodeify(done);
},
};

View File

@ -0,0 +1,62 @@
Promise = require('bluebird')
_ = require('lodash')
form = require('resin-cli-form')
chalk = require('chalk')
dockerUtils = require('../../utils/docker')
{ exitWithExpectedError } = require('../../utils/patterns')
exports.dockerPort = dockerPort = 2375
exports.dockerTimeout = dockerTimeout = 2000
exports.filterOutSupervisorContainer = filterOutSupervisorContainer = (container) ->
for name in container.Names
return false if name.includes('resin_supervisor')
return true
exports.selectContainerFromDevice = Promise.method (deviceIp, filterSupervisor = false) ->
docker = dockerUtils.createClient(host: deviceIp, port: dockerPort, timeout: dockerTimeout)
# List all containers, including those not running
docker.listContainersAsync(all: true)
.filter (container) ->
return true if not filterSupervisor
filterOutSupervisorContainer(container)
.then (containers) ->
if _.isEmpty(containers)
exitWithExpectedError("No containers found in #{deviceIp}")
return form.ask
message: 'Select a container'
type: 'list'
choices: _.map containers, (container) ->
containerName = container.Names?[0] or 'Untitled'
shortContainerId = ('' + container.Id).substr(0, 11)
return {
name: "#{containerName} (#{shortContainerId})"
value: container.Id
}
exports.pipeContainerStream = Promise.method ({ deviceIp, name, outStream, follow = false }) ->
docker = dockerUtils.createClient(host: deviceIp, port: dockerPort)
container = docker.getContainer(name)
container.inspectAsync()
.then (containerInfo) ->
return containerInfo?.State?.Running
.then (isRunning) ->
container.attachAsync
logs: not follow or not isRunning
stream: follow and isRunning
stdout: true
stderr: true
.then (containerStream) ->
containerStream.pipe(outStream)
.catch (err) ->
err = '' + err.statusCode
if err is '404'
return console.log(chalk.red.bold("Container '#{name}' not found."))
throw err
exports.getSubShellCommand = require('../../utils/helpers').getSubShellCommand

View File

@ -0,0 +1,230 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
BOOT_PARTITION = 1
CONNECTIONS_FOLDER = '/system-connections'
getConfigurationSchema = (connnectionFileName = 'resin-wifi') ->
mapper: [
{
template:
persistentLogging: '{{persistentLogging}}'
domain: [
[ 'config_json', 'persistentLogging' ]
]
}
{
template:
hostname: '{{hostname}}'
domain: [
[ 'config_json', 'hostname' ]
]
}
{
template:
wifi:
ssid: '{{networkSsid}}'
'wifi-security':
psk: '{{networkKey}}'
domain: [
[ 'system_connections', connnectionFileName, 'wifi' ]
[ 'system_connections', connnectionFileName, 'wifi-security' ]
]
}
]
files:
system_connections:
fileset: true
type: 'ini'
location:
path: CONNECTIONS_FOLDER.slice(1)
# Reconfix still uses the older resin-image-fs, so still needs an
# object-based partition definition.
partition: BOOT_PARTITION
config_json:
type: 'json'
location:
path: 'config.json'
partition: BOOT_PARTITION
inquirerOptions = (data) -> [
{
message: 'Network SSID'
type: 'input'
name: 'networkSsid'
default: data.networkSsid
}
{
message: 'Network Key'
type: 'input'
name: 'networkKey'
default: data.networkKey
}
{
message: 'Do you want to set advanced settings?'
type: 'confirm'
name: 'advancedSettings'
default: false
}
{
message: 'Device Hostname'
type: 'input'
name: 'hostname'
default: data.hostname,
when: (answers) ->
answers.advancedSettings
}
{
message: 'Do you want to enable persistent logging?'
type: 'confirm'
name: 'persistentLogging'
default: data.persistentLogging
when: (answers) ->
answers.advancedSettings
}
]
getConfiguration = (data) ->
_ = require('lodash')
inquirer = require('inquirer')
# `persistentLogging` can be `undefined`, so we want
# to make sure that case defaults to `false`
data = _.assign data,
persistentLogging: data.persistentLogging or false
inquirer.prompt(inquirerOptions(data))
.then (answers) ->
return _.merge(data, answers)
# Taken from https://goo.gl/kr1kCt
CONNECTION_FILE = '''
[connection]
id=resin-wifi
type=wifi
[wifi]
hidden=true
mode=infrastructure
ssid=My_Wifi_Ssid
[wifi-security]
auth-alg=open
key-mgmt=wpa-psk
psk=super_secret_wifi_password
[ipv4]
method=auto
[ipv6]
addr-gen-mode=stable-privacy
method=auto
'''
###
* if the `resin-wifi` file exists (previously configured image or downloaded from the UI) it's used and reconfigured
* if the `resin-sample.ignore` exists it's copied to `resin-wifi`
* if the `resin-sample` exists it's reconfigured (legacy mode, will be removed eventually)
* otherwise, the new file is created
###
prepareConnectionFile = (target) ->
_ = require('lodash')
imagefs = require('resin-image-fs')
imagefs.listDirectory
image: target
partition: BOOT_PARTITION
path: CONNECTIONS_FOLDER
.then (files) ->
# The required file already exists
if _.includes(files, 'resin-wifi')
return null
# Fresh image, new mode, accoding to https://github.com/resin-os/meta-resin/pull/770/files
if _.includes(files, 'resin-sample.ignore')
return imagefs.copy
image: target
partition: BOOT_PARTITION
path: "#{CONNECTIONS_FOLDER}/resin-sample.ignore"
,
image: target
partition: BOOT_PARTITION
path: "#{CONNECTIONS_FOLDER}/resin-wifi"
.thenReturn(null)
# Legacy mode, to be removed later
# We return the file name override from this branch
# When it is removed the following cleanup should be done:
# * delete all the null returns from this method
# * turn `getConfigurationSchema` back into the constant, with the connection filename always being `resin-wifi`
# * drop the final `then` from this method
# * adapt the code in the main listener to not receive the config from this method, and use that constant instead
if _.includes(files, 'resin-sample')
return 'resin-sample'
# In case there's no file at all (shouldn't happen normally, but the file might have been removed)
return imagefs.writeFile
image: target
partition: BOOT_PARTITION
path: "#{CONNECTIONS_FOLDER}/resin-wifi"
, CONNECTION_FILE
.thenReturn(null)
.then (connectionFileName) ->
return getConfigurationSchema(connectionFileName)
removeHostname = (schema) ->
_ = require('lodash')
schema.mapper = _.reject schema.mapper, (mapper) ->
_.isEqual(Object.keys(mapper.template), ['hostname'])
module.exports =
signature: 'local configure <target>'
description: '(Re)configure a resinOS drive or image'
help: '''
Use this command to configure or reconfigure a resinOS drive or image.
Examples:
$ resin local configure /dev/sdc
$ resin local configure path/to/image.img
'''
root: true
action: (params, options, done) ->
Promise = require('bluebird')
umount = require('umount')
umountAsync = Promise.promisify(umount.umount)
isMountedAsync = Promise.promisify(umount.isMounted)
reconfix = require('reconfix')
denymount = Promise.promisify(require('denymount'))
prepareConnectionFile(params.target)
.tap ->
isMountedAsync(params.target).then (isMounted) ->
return if not isMounted
umountAsync(params.target)
.then (configurationSchema) ->
denymount params.target, (cb) ->
reconfix.readConfiguration(configurationSchema, params.target)
.then(getConfiguration)
.then (answers) ->
if not answers.hostname
removeHostname(configurationSchema)
reconfix.writeConfiguration(configurationSchema, answers, params.target)
.asCallback(cb)
.then ->
console.log('Done!')
.asCallback(done)

View File

@ -0,0 +1,120 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the 'License');
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an 'AS IS' BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
module.exports =
signature: 'local flash <image>'
description: 'Flash an image to a drive'
help: '''
Use this command to flash a resinOS image to a drive.
Examples:
$ resin local flash path/to/resinos.img
$ resin local flash path/to/resinos.img --drive /dev/disk2
$ resin local flash path/to/resinos.img --drive /dev/disk2 --yes
'''
options: [
signature: 'yes'
boolean: true
description: 'confirm non-interactively'
alias: 'y'
,
signature: 'drive'
parameter: 'drive'
description: 'drive'
alias: 'd'
]
root: true
action: (params, options, done) ->
_ = require('lodash')
os = require('os')
Promise = require('bluebird')
umountAsync = Promise.promisify(require('umount').umount)
fs = Promise.promisifyAll(require('fs'))
driveListAsync = Promise.promisify(require('drivelist').list)
chalk = require('chalk')
visuals = require('resin-cli-visuals')
form = require('resin-cli-form')
imageWrite = require('etcher-image-write')
form.run [
{
message: 'Select drive'
type: 'drive'
name: 'drive'
},
{
message: 'This will erase the selected drive. Are you sure?'
type: 'confirm'
name: 'yes'
default: false
}
],
override:
drive: options.drive
# If `options.yes` is `false`, pass `undefined`,
# otherwise the question will not be asked because
# `false` is a defined value.
yes: options.yes || undefined
# TODO: dedupe with the resin-device-operations
.then (answers) ->
if answers.yes isnt true
console.log(chalk.red.bold('Aborted image flash'))
process.exit(0)
driveListAsync().then (drives) ->
selectedDrive = _.find(drives, device: answers.drive)
if not selectedDrive?
throw new Error("Drive not found: #{answers.drive}")
return selectedDrive
.then (selectedDrive) ->
progressBars =
write: new visuals.Progress('Flashing')
check: new visuals.Progress('Validating')
umountAsync(selectedDrive.device).then ->
Promise.props
imageSize: fs.statAsync(params.image).get('size'),
imageStream: Promise.resolve(fs.createReadStream(params.image))
driveFileDescriptor: fs.openAsync(selectedDrive.raw, 'rs+')
.then (results) ->
imageWrite.write
fd: results.driveFileDescriptor
device: selectedDrive.raw
size: selectedDrive.size
,
stream: results.imageStream,
size: results.imageSize
,
check: true
.then (writer) ->
new Promise (resolve, reject) ->
writer.on 'progress', (state) ->
progressBars[state.type].update(state)
writer.on('error', reject)
writer.on('done', resolve)
.then ->
if (os.platform() is 'win32') and selectedDrive.mountpoint?
ejectAsync = Promise.promisify(require('removedrive').eject)
return ejectAsync(selectedDrive.mountpoint)
return umountAsync(selectedDrive.device)
.asCallback(done)

View File

@ -0,0 +1,23 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
exports.configure = require('./configure')
exports.flash = require('./flash')
exports.logs = require('./logs')
exports.scan = require('./scan')
exports.ssh = require('./ssh')
exports.push = require('./push')
exports.stop = require('./stop')

View File

@ -0,0 +1,66 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
# A function to reliably execute a command
# in all supported operating systems, including
# different Windows environments like `cmd.exe`
# and `Cygwin` should be encapsulated in a
# re-usable package.
#
module.exports =
signature: 'local logs [deviceIp]'
description: 'Get or attach to logs of a running container on a resinOS device'
help: '''
Examples:
$ resin local logs
$ resin local logs -f
$ resin local logs 192.168.1.10
$ resin local logs 192.168.1.10 -f
$ resin local logs 192.168.1.10 -f --app-name myapp
'''
options: [
signature: 'follow'
boolean: true
description: 'follow log'
alias: 'f'
,
signature: 'app-name'
parameter: 'name'
description: 'name of container to get logs from'
alias: 'a'
]
root: true
action: (params, options, done) ->
Promise = require('bluebird')
{ forms } = require('resin-sync')
{ selectContainerFromDevice, pipeContainerStream } = require('./common')
Promise.try ->
if not params.deviceIp?
return forms.selectLocalResinOsDevice()
return params.deviceIp
.then (@deviceIp) =>
if not options['app-name']?
return selectContainerFromDevice(@deviceIp)
return options['app-name']
.then (appName) =>
pipeContainerStream
deviceIp: @deviceIp
name: appName
outStream: process.stdout
follow: options['follow']

View File

@ -0,0 +1,78 @@
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
# Loads '.resin-sync.yml' configuration from 'source' directory.
# Returns the configuration object on success
#
_ = require('lodash')
resinPush = require('resin-sync').capitano('resin-toolbox')
# TODO: This is a temporary workaround to reuse the existing `rdt push`
# capitano frontend in `resin local push`.
resinPushHelp = '''
Warning: 'resin local push' requires an openssh-compatible client and 'rsync' to
be correctly installed in your shell environment. For more information (including
Windows support) please check the README here: https://github.com/resin-io/resin-cli
Use this command to push your local changes to a container on a LAN-accessible resinOS device on the fly.
If `Dockerfile` or any file in the 'build-triggers' list is changed,
a new container will be built and run on your device.
If not, changes will simply be synced with `rsync` into the application container.
After every 'resin local push' the updated settings will be saved in
'<source>/.resin-sync.yml' and will be used in later invocations. You can
also change any option by editing '.resin-sync.yml' directly.
Here is an example '.resin-sync.yml' :
$ cat $PWD/.resin-sync.yml
local_resinos:
app-name: local-app
build-triggers:
- Dockerfile: file-hash-abcdefabcdefabcdefabcdefabcdefabcdef
- package.json: file-hash-abcdefabcdefabcdefabcdefabcdefabcdef
environment:
- MY_VARIABLE=123
Command line options have precedence over the ones saved in '.resin-sync.yml'.
If '.gitignore' is found in the source directory then all explicitly listed files will be
excluded when using rsync to update the container. You can choose to change this default behavior with the
'--skip-gitignore' option.
Examples:
$ resin local push
$ resin local push --app-name test-server --build-triggers package.json,requirements.txt
$ resin local push --force-build
$ resin local push --force-build --skip-logs
$ resin local push --ignore lib/
$ resin local push --verbose false
$ resin local push 192.168.2.10 --source . --destination /usr/src/app
$ resin local push 192.168.2.10 -s /home/user/myResinProject -d /usr/src/app --before 'echo Hello' --after 'echo Done'
'''
module.exports = _.assign resinPush,
signature: 'local push [deviceIp]'
help: resinPushHelp
primary: true
root: true

View File

@ -0,0 +1,100 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
dockerInfoProperties = [
'Containers'
'ContainersRunning'
'ContainersPaused'
'ContainersStopped'
'Images'
'Driver'
'SystemTime'
'KernelVersion'
'OperatingSystem'
'Architecture'
]
dockerVersionProperties = [
'Version'
'ApiVersion'
]
module.exports =
signature: 'local scan'
description: 'Scan for resinOS devices in your local network'
help: '''
Examples:
$ resin local scan
$ resin local scan --timeout 120
$ resin local scan --verbose
'''
options: [
signature: 'verbose'
boolean: true
description: 'Display full info'
alias: 'v'
,
signature: 'timeout'
parameter: 'timeout'
description: 'Scan timeout in seconds'
alias: 't'
]
primary: true
root: true
action: (params, options, done) ->
Promise = require('bluebird')
_ = require('lodash')
prettyjson = require('prettyjson')
{ discover } = require('resin-sync')
{ SpinnerPromise } = require('resin-cli-visuals')
{ dockerPort, dockerTimeout } = require('./common')
dockerUtils = require('../../utils/docker')
{ exitWithExpectedError } = require('../../utils/patterns')
if options.timeout?
options.timeout *= 1000
Promise.try ->
new SpinnerPromise
promise: discover.discoverLocalResinOsDevices(options.timeout)
startMessage: 'Scanning for local resinOS devices..'
stopMessage: 'Reporting scan results'
.filter ({ address }) ->
Promise.try ->
docker = dockerUtils.createClient(host: address, port: dockerPort, timeout: dockerTimeout)
docker.pingAsync()
.return(true)
.catchReturn(false)
.tap (devices) ->
if _.isEmpty(devices)
exitWithExpectedError('Could not find any resinOS devices in the local network')
.map ({ host, address }) ->
docker = dockerUtils.createClient(host: address, port: dockerPort, timeout: dockerTimeout)
Promise.props
dockerInfo: docker.infoAsync().catchReturn('Could not get Docker info')
dockerVersion: docker.versionAsync().catchReturn('Could not get Docker version')
.then ({ dockerInfo, dockerVersion }) ->
if not options.verbose
dockerInfo = _.pick(dockerInfo, dockerInfoProperties) if _.isObject(dockerInfo)
dockerVersion = _.pick(dockerVersion, dockerVersionProperties) if _.isObject(dockerVersion)
return { host, address, dockerInfo, dockerVersion }
.then (devicesInfo) ->
console.log(prettyjson.render(devicesInfo, noColor: true))
.nodeify(done)

View File

@ -0,0 +1,114 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
{ hostOSAccess } = require('../command-options')
_ = require('lodash')
localHostOSAccessOption = _.cloneDeep(hostOSAccess)
localHostOSAccessOption.description = 'get a shell into the host OS'
module.exports =
signature: 'local ssh [deviceIp]'
description: 'Get a shell into a resinOS device'
help: '''
Warning: 'resin local ssh' requires an openssh-compatible client to be correctly
installed in your shell environment. For more information (including Windows
support) please check the README here: https://github.com/resin-io/resin-cli
Use this command to get a shell into the running application container of
your device.
The '--host' option will get you a shell into the Host OS of the resinOS device.
No option will return a list of containers to enter or you can explicitly select
one by passing its name to the --container option
Examples:
$ resin local ssh
$ resin local ssh --host
$ resin local ssh --container chaotic_water
$ resin local ssh --container chaotic_water --port 22222
$ resin local ssh --verbose
'''
options: [
signature: 'verbose'
boolean: true
description: 'increase verbosity'
alias: 'v'
,
localHostOSAccessOption,
signature: 'container'
parameter: 'container'
default: null
description: 'name of container to access'
alias: 'c'
,
signature: 'port'
parameter: 'port'
description: 'ssh port number (default: 22222)'
alias: 'p'
]
root: true
action: (params, options, done) ->
child_process = require('child_process')
Promise = require 'bluebird'
_ = require('lodash')
{ forms } = require('resin-sync')
{ selectContainerFromDevice, getSubShellCommand } = require('./common')
{ exitWithExpectedError } = require('../../utils/patterns')
if (options.host is true and options.container?)
exitWithExpectedError('Please pass either --host or --container option')
if not options.port?
options.port = 22222
verbose = if options.verbose then '-vvv' else ''
Promise.try ->
if not params.deviceIp?
return forms.selectLocalResinOsDevice()
return params.deviceIp
.then (deviceIp) ->
_.assign(options, { deviceIp })
return if options.host
if not options.container?
return selectContainerFromDevice(deviceIp)
return options.container
.then (container) ->
command = "ssh \
#{verbose} \
-t \
-p #{options.port} \
-o LogLevel=ERROR \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
root@#{options.deviceIp}"
if not options.host
shellCmd = '''/bin/sh -c $"'if [ -e /bin/bash ]; then exec /bin/bash; else exec /bin/sh; fi'"'''
dockerCmd = "'$(if [ -f /usr/bin/balena ]; then echo \"balena\"; else echo \"docker\"; fi)'"
command += " #{dockerCmd} exec -ti #{container} #{shellCmd}"
subShellCommand = getSubShellCommand(command)
child_process.spawn subShellCommand.program, subShellCommand.args,
stdio: 'inherit'
.nodeify(done)

View File

@ -0,0 +1,79 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
# A function to reliably execute a command
# in all supported operating systems, including
# different Windows environments like `cmd.exe`
# and `Cygwin` should be encapsulated in a
# re-usable package.
#
module.exports =
signature: 'local stop [deviceIp]'
description: 'Stop a running container on a resinOS device'
help: '''
Examples:
$ resin local stop
$ resin local stop --app-name myapp
$ resin local stop --all
$ resin local stop 192.168.1.10
$ resin local stop 192.168.1.10 --app-name myapp
'''
options: [
signature: 'all'
boolean: true
description: 'stop all containers'
,
signature: 'app-name'
parameter: 'name'
description: 'name of container to stop'
alias: 'a'
]
root: true
action: (params, options, done) ->
Promise = require('bluebird')
chalk = require('chalk')
{ forms, config, ResinLocalDockerUtils } = require('resin-sync')
{ selectContainerFromDevice, filterOutSupervisorContainer } = require('./common')
Promise.try ->
if not params.deviceIp?
return forms.selectLocalResinOsDevice()
return params.deviceIp
.then (@deviceIp) =>
@docker = new ResinLocalDockerUtils(@deviceIp)
if options.all
# Only list running containers
return @docker.docker.listContainersAsync(all: false)
.filter(filterOutSupervisorContainer)
.then (containers) =>
Promise.map containers, ({ Names, Id }) =>
console.log(chalk.yellow.bold("* Stopping container #{Names[0]}"))
@docker.stopContainer(Id)
ymlConfig = config.load()
@appName = options['app-name'] ? ymlConfig['local_resinos']?['app-name']
@docker.checkForRunningContainer(@appName)
.then (isRunning) =>
if not isRunning
return selectContainerFromDevice(@deviceIp, true)
console.log(chalk.yellow.bold("* Stopping container #{@appName}"))
return @appName
.then (runningContainerName) =>
@docker.stopContainer(runningContainerName)

View File

@ -1,9 +1,22 @@
_ = require('lodash')
resin = require('resin-sdk')
###
Copyright 2016-2017 Resin.io
LOGS_HISTORY_COUNT = 200
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
exports.logs =
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
{ normalizeUuidProp } = require('../utils/normalization')
module.exports =
signature: 'logs <uuid>'
description: 'show device logs'
help: '''
@ -11,29 +24,14 @@ exports.logs =
By default, the command prints all log messages and exit.
To limit the output to the n last lines, use the `--num` option along with a number.
This is similar to doing `resin logs <uuid> | tail -n X`.
To continuously stream output, and see new logs in real time, use the `--tail` option.
Note that for now you need to provide the whole UUID for this command to work correctly,
and the tool won't notice if you're using an invalid UUID.
This is due to some technical limitations that we plan to address soon.
Examples:
$ resin logs 23c73a12e3527df55c60b9ce647640c1b7da1b32d71e6a39849ac0f00db828
$ resin logs 23c73a12e3527df55c60b9ce647640c1b7da1b32d71e6a39849ac0f00db828 --num 20
$ resin logs 23c73a12e3527df55c60b9ce647640c1b7da1b32d71e6a39849ac0f00db828 --tail
$ resin logs 23c73a1
$ resin logs 23c73a1
'''
options: [
{
signature: 'num'
parameter: 'num'
description: 'number of lines to display'
alias: 'n'
}
{
signature: 'tail'
description: 'continuously stream output'
@ -42,15 +40,22 @@ exports.logs =
}
]
permission: 'user'
primary: true
action: (params, options, done) ->
resin.logs.subscribe params.uuid, {
history: options.num or LOGS_HISTORY_COUNT
tail: options.tail
}, (error, message) ->
return done(error) if error?
if _.isArray(message)
_.each message, (line) ->
console.log(line)
else
console.log(message)
return done()
normalizeUuidProp(params)
resin = require('resin-sdk').fromSharedOptions()
moment = require('moment')
printLine = (line) ->
timestamp = moment(line.timestamp).format('DD.MM.YY HH:mm:ss (ZZ)')
console.log("#{timestamp} #{line.message}")
if options.tail
resin.logs.subscribe(params.uuid, { count: 100 }).then (logs) ->
logs.on('line', printLine)
logs.on('error', done)
.catch(done)
else
resin.logs.history(params.uuid)
.each(printLine)
.catch(done)

View File

@ -1,5 +1,20 @@
async = require('async')
resin = require('resin-sdk')
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
{ normalizeUuidProp } = require('../utils/normalization')
exports.set =
signature: 'note <|note>'
@ -9,20 +24,32 @@ exports.set =
If note command isn't passed, the tool attempts to read from `stdin`.
To view the notes, use $ resin device <name>.
To view the notes, use $ resin device <uuid>.
Examples:
$ resin note "My useful note" --device MyDevice
$ cat note.txt | resin note --device MyDevice
$ resin note "My useful note" --device 7cf02a6
$ cat note.txt | resin note --device 7cf02a6
'''
options: [
signature: 'device'
parameter: 'device'
description: 'device name'
description: 'device uuid'
alias: [ 'd', 'dev' ]
required: 'You have to specify a device'
]
permission: 'user'
action: (params, options, done) ->
resin.models.device.note(options.device, params.note, done)
normalizeUuidProp(options, 'device')
Promise = require('bluebird')
_ = require('lodash')
resin = require('resin-sdk').fromSharedOptions()
{ exitWithExpectedError } = require('../utils/patterns')
Promise.try ->
if _.isEmpty(params.note)
exitWithExpectedError('Missing note content')
resin.models.device.note(options.device, params.note)
.nodeify(done)

View File

@ -1,182 +1,382 @@
_ = require('lodash-contrib')
os = require('os')
async = require('async')
path = require('path')
mkdirp = require('mkdirp')
resin = require('resin-sdk')
visuals = require('resin-cli-visuals')
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
commandOptions = require('./command-options')
npm = require('../npm')
packageJSON = require('../../package.json')
updateActions = require('./update')
elevate = require('../elevate')
_ = require('lodash')
{ normalizeUuidProp } = require('../utils/normalization')
formatVersion = (v, isRecommended) ->
result = "v#{v}"
if isRecommended
result += ' (recommended)'
return result
resolveVersion = (deviceType, version) ->
if version isnt 'menu'
if version[0] == 'v'
version = version.slice(1)
return Promise.resolve(version)
form = require('resin-cli-form')
resin = require('resin-sdk').fromSharedOptions()
resin.models.os.getSupportedVersions(deviceType)
.then ({ versions, recommended }) ->
choices = versions.map (v) ->
value: v
name: formatVersion(v, v is recommended)
return form.ask
message: 'Select the OS version:'
type: 'list'
choices: choices
default: recommended
exports.versions =
signature: 'os versions <type>'
description: 'show the available resinOS versions for the given device type'
help: '''
Use this command to show the available resinOS versions for a certain device type.
Check available types with `resin devices supported`
Example:
$ resin os versions raspberrypi3
'''
action: (params, options, done) ->
resin = require('resin-sdk').fromSharedOptions()
resin.models.os.getSupportedVersions(params.type)
.then ({ versions, recommended }) ->
versions.forEach (v) ->
console.log(formatVersion(v, v is recommended))
exports.download =
signature: 'os download <id>'
description: 'download device OS'
signature: 'os download <type>'
description: 'download an unconfigured os image'
help: '''
Use this command to download the device OS configured to a specific network.
Use this command to download an unconfigured os image for a certain device type.
Check available types with `resin devices supported`
Ethernet:
You can setup the device OS to use ethernet by setting the `--network` option to "ethernet".
If version is not specified the newest stable (non-pre-release) version of OS
is downloaded if available, or the newest version otherwise (if all existing
versions for the given device type are pre-release).
Wifi:
You can setup the device OS to use wifi by setting the `--network` option to "wifi".
If you set "network" to "wifi", you will need to specify the `--ssid` and `--key` option as well.
Alternatively, you can omit all kind of network configuration options to configure interactively.
You have to specify an output location with the `--output` option.
You can pass `--version menu` to pick the OS version from the interactive menu
of all available versions.
Examples:
$ resin os download 91 --output ~/MyResinOS.zip
$ resin os download 91 --network ethernet --output ~/MyResinOS.zip
$ resin os download 91 --network wifi --ssid MyNetwork --key secreykey123 --output ~/MyResinOS.zip
$ resin os download 91 --network ethernet --output ~/MyResinOS.zip
$ resin os download raspberrypi3 -o ../foo/bar/raspberry-pi.img
$ resin os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version 1.24.1
$ resin os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version ^1.20.0
$ resin os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version latest
$ resin os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version default
$ resin os download raspberrypi3 -o ../foo/bar/raspberry-pi.img --version menu
'''
permission: 'user'
options: [
commandOptions.network
commandOptions.wifiSsid
commandOptions.wifiKey
{
signature: 'output'
description: 'output path'
parameter: 'output'
description: 'output file'
alias: 'o'
required: 'You need to specify an output file'
required: 'You have to specify the output location'
}
commandOptions.osVersion
]
action: (params, options, done) ->
Promise = require('bluebird')
unzip = require('unzip2')
fs = require('fs')
rindle = require('rindle')
manager = require('resin-image-manager')
visuals = require('resin-cli-visuals')
console.info("Getting device operating system for #{params.type}")
displayVersion = ''
Promise.try ->
if not options.version
console.warn('OS version is not specified, using the default version:
the newest stable (non-pre-release) version if available,
or the newest version otherwise (if all existing
versions for the given device type are pre-release).')
return 'default'
return resolveVersion(params.type, options.version)
.then (version) ->
if version isnt 'default'
displayVersion = " #{version}"
return manager.get(params.type, version)
.then (stream) ->
bar = new visuals.Progress("Downloading Device OS#{displayVersion}")
spinner = new visuals.Spinner("Downloading Device OS#{displayVersion} (size unknown)")
stream.on 'progress', (state) ->
if state?
bar.update(state)
else
spinner.start()
stream.on 'end', ->
spinner.stop()
# We completely rely on the `mime` custom property
# to make this decision.
# The actual stream should be checked instead.
if stream.mime is 'application/zip'
output = unzip.Extract(path: options.output)
else
output = fs.createWriteStream(options.output)
return rindle.wait(stream.pipe(output)).return(options.output)
.tap (output) ->
console.info('The image was downloaded successfully')
.nodeify(done)
buildConfig = (image, deviceType, advanced = false) ->
form = require('resin-cli-form')
helpers = require('../utils/helpers')
helpers.getManifest(image, deviceType)
.get('options')
.then (questions) ->
if not advanced
advancedGroup = _.find questions,
name: 'advanced'
isGroup: true
if advancedGroup?
override = helpers.getGroupDefaults(advancedGroup)
return form.run(questions, { override })
exports.buildConfig =
signature: 'os build-config <image> <device-type>'
description: 'build the OS config and save it to the JSON file'
help: '''
Use this command to prebuild the OS config once and skip the interactive part of `resin os configure`.
Example:
$ resin os build-config ../path/rpi3.img raspberrypi3 --output rpi3-config.json
$ resin os configure ../path/rpi3.img 7cf02a6 --config "$(cat rpi3-config.json)"
'''
permission: 'user'
options: [
commandOptions.advancedConfig
{
signature: 'output'
description: 'the path to the output JSON file'
alias: 'o'
required: 'the output path is required'
parameter: 'output'
}
]
permission: 'user'
action: (params, options, done) ->
osParams =
network: options.network
wifiSsid: options.ssid
wifiKey: options.key
appId: params.id
fs = require('fs')
Promise = require('bluebird')
writeFileAsync = Promise.promisify(fs.writeFile)
async.waterfall [
buildConfig(params.image, params['device-type'], options.advanced)
.then (answers) ->
writeFileAsync(options.output, JSON.stringify(answers, null, 4))
.nodeify(done)
(callback) ->
return callback() if osParams.network?
visuals.patterns.selectNetworkParameters (error, parameters) ->
return callback(error) if error?
_.extend(osParams, parameters)
return callback()
(callback) ->
# We need to ensure this directory exists
mkdirp(path.dirname(options.output), _.unary(callback))
(callback) ->
console.info("Destination file: #{options.output}\n")
bar = new visuals.widgets.Progress('Downloading Device OS')
spinner = new visuals.widgets.Spinner('Downloading Device OS (size unknown)')
resin.models.os.download osParams, options.output, (error) ->
spinner.stop()
return callback(error) if error?
, (state) ->
if state?
bar.update(state)
else
spinner.start()
], (error) ->
return done(error) if error?
console.info("\nFinished downloading #{options.output}")
return done(null, options.output)
exports.install =
signature: 'os install <image> [device]'
description: 'write an operating system image to a device'
exports.configure =
signature: 'os configure <image> [uuid] [deviceApiKey]'
description: 'configure an os image'
help: '''
Use this command to write an operating system image to a device.
Use this command to configure a previously downloaded operating system image for
the specific device or for an application generally.
Note that this command requires admin privileges.
Calling this command without --version is not recommended, and may fail in
future releases if the OS version cannot be inferred.
If `device` is omitted, you will be prompted to select a device interactively.
Note that device api keys are only supported on ResinOS 2.0.3+.
Notice this command asks for confirmation interactively.
You can avoid this by passing the `--yes` boolean option.
You can quiet the progress bar by passing the `--quiet` boolean option.
You may have to unmount the device before attempting this operation.
See the `drives` command to get a list of all connected devices to your machine and their respective ids.
In Mac OS X:
$ sudo diskutil unmountDisk /dev/xxx
In GNU/Linux:
$ sudo umount /dev/xxx
This command still supports the *deprecated* format where the UUID and optionally device key
are passed directly on the command line, but the recommended way is to pass either an --app or
--device argument. The deprecated format will be remove in a future release.
Examples:
$ resin os install rpi.iso /dev/disk2
$ resin os configure ../path/rpi.img --device 7cf02a6 --version 2.12.7
$ resin os configure ../path/rpi.img --device 7cf02a6 --version 2.12.7 --device-api-key <existingDeviceKey>
$ resin os configure ../path/rpi.img --app MyApp --version 2.12.7
'''
options: [ commandOptions.yes ]
permission: 'user'
options: [
commandOptions.advancedConfig
commandOptions.optionalApplication
commandOptions.optionalDevice
commandOptions.optionalDeviceApiKey
commandOptions.optionalOsVersion
{
signature: 'config'
description: 'path to the config JSON file, see `resin os build-config`'
parameter: 'config'
}
]
action: (params, options, done) ->
normalizeUuidProp(params)
normalizeUuidProp(options, 'device')
fs = require('fs')
Promise = require('bluebird')
readFileAsync = Promise.promisify(fs.readFile)
resin = require('resin-sdk').fromSharedOptions()
init = require('resin-device-init')
helpers = require('../utils/helpers')
patterns = require('../utils/patterns')
{ generateDeviceConfig, generateApplicationConfig } = require('../utils/config')
bundle = require('../devices/raspberry-pi')
if _.filter([
options.device
options.application
params.uuid
]).length != 1
patterns.exitWithExpectedError '''
To configure an image, you must provide exactly one of:
async.waterfall [
* A device, with --device <uuid>
* An application, with --app <appname>
* [Deprecated] A device, passing its uuid directly on the command line
(callback) ->
npm.isUpdated(packageJSON.name, packageJSON.version, callback)
See the help page for examples:
(isUpdated, callback) ->
return callback() if isUpdated
$ resin help os configure
'''
if params.uuid
console.warn(
'Directly passing a UUID to `resin os configure` is deprecated, and will stop working in future.\n' +
'Pass your device UUID with --device <uuid> instead.' +
if params.deviceApiKey
' Device api keys can be passed with --deviceApiKey.\n'
else '\n'
)
console.info '''
Resin CLI is outdated.
uuid = options.device || params.uuid
deviceApiKey = options.deviceApiKey || params.deviceApiKey
In order to avoid device compatibility issues, this command
requires that you have the Resin CLI updated.
console.info('Configuring operating system image')
Updating now...
'''
configurationResourceType = if uuid then 'device' else 'application'
updateActions.update.action(params, options, _.unary(callback))
resin.models[configurationResourceType].get(uuid || options.application)
.then (appOrDevice) ->
Promise.try ->
if options.config
return readFileAsync(options.config, 'utf8')
.then(JSON.parse)
return buildConfig(params.image, appOrDevice.device_type, options.advanced)
.then (answers) ->
answers.version = options.version
(callback) ->
return callback(null, params.device) if params.device?
(if configurationResourceType == 'device'
generateDeviceConfig(appOrDevice, deviceApiKey, answers)
else
generateApplicationConfig(appOrDevice, answers)
).then (config) ->
init.configure(params.image, appOrDevice.device_type, config, answers)
.then(helpers.osProgressHandler)
.nodeify(done)
# TODO: See if we can reuse the drives action somehow here
visuals.patterns.selectDrive (error, device) ->
return callback(error) if error?
INIT_WARNING_MESSAGE = '''
Note: Initializing the device may ask for administrative permissions
because we need to access the raw devices directly.
'''
if not device?
return callback(new Error('No removable devices available'))
exports.initialize =
signature: 'os initialize <image>'
description: 'initialize an os image'
help: """
Use this command to initialize a device with previously configured operating system image.
return callback(null, device)
#{INIT_WARNING_MESSAGE}
(device, callback) ->
params.device = device
message = "This will completely erase #{params.device}. Are you sure you want to continue?"
visuals.patterns.confirm(options.yes, message, callback)
Examples:
(confirmed, callback) ->
return done() if not confirmed
$ resin os initialize ../path/rpi.img --type 'raspberry-pi'
"""
permission: 'user'
options: [
commandOptions.yes
{
signature: 'type'
description: 'device type (Check available types with `resin devices supported`)'
parameter: 'type'
alias: 't'
required: 'You have to specify a device type'
}
commandOptions.drive
]
action: (params, options, done) ->
Promise = require('bluebird')
umountAsync = Promise.promisify(require('umount').umount)
form = require('resin-cli-form')
patterns = require('../utils/patterns')
helpers = require('../utils/helpers')
bar = new visuals.widgets.Progress('Writing Device OS')
params.progress = _.bind(bar.update, bar)
bundle.write(params, callback)
console.info("""
Initializing device
], (error) ->
return done() if not error?
#{INIT_WARNING_MESSAGE}
""")
helpers.getManifest(params.image, options.type)
.then (manifest) ->
return manifest.initialization?.options
.then (questions) ->
return form.run questions,
override:
drive: options.drive
.tap (answers) ->
return if not answers.drive?
patterns.confirm(
options.yes
"This will erase #{answers.drive}. Are you sure?"
"Going to erase #{answers.drive}."
)
.return(answers.drive)
.then(umountAsync)
.tap (answers) ->
return helpers.sudo([
'internal'
'osinit'
params.image
options.type
JSON.stringify(answers)
])
.then (answers) ->
return if not answers.drive?
if elevate.shouldElevate(error) and not options.fromScript
# TODO: resin local makes use of ejectAsync, see below
# DO we need this / should we do that here?
# Need to escape every path to avoid errors
resinWritePath = "\"#{path.join(__dirname, '..', '..', 'bin', 'resin-write')}\""
elevate.run("\"#{process.argv[0]}\" #{resinWritePath} \"#{params.image}\" \"#{params.device}\"")
else
return done(error)
# getDrive = (drive) ->
# driveListAsync().then (drives) ->
# selectedDrive = _.find(drives, device: drive)
# if not selectedDrive?
# throw new Error("Drive not found: #{drive}")
# return selectedDrive
# if (os.platform() is 'win32') and selectedDrive.mountpoint?
# ejectAsync = Promise.promisify(require('removedrive').eject)
# return ejectAsync(selectedDrive.mountpoint)
umountAsync(answers.drive).tap ->
console.info("You can safely remove #{answers.drive} now")
.nodeify(done)

View File

@ -1,94 +0,0 @@
_ = require('lodash')
visuals = require('resin-cli-visuals')
commandOptions = require('./command-options')
plugins = require('../plugins')
exports.list =
signature: 'plugins'
description: 'list all plugins'
help: '''
Use this command to list all the installed resin plugins.
Examples:
$ resin plugins
'''
permission: 'user'
action: (params, options, done) ->
plugins.list (error, resinPlugins) ->
return done(error) if error?
if _.isEmpty(resinPlugins)
console.log('You don\'t have any plugins yet')
return done()
console.log visuals.widgets.table.horizontal resinPlugins, [
'name'
'version'
'description'
'license'
]
return done()
exports.install =
signature: 'plugin install <name>'
description: 'install a plugin'
help: '''
Use this command to install a resin plugin
Use `--quiet` to prevent information logging.
Examples:
$ resin plugin install hello
'''
permission: 'user'
action: (params, options, done) ->
plugins.install params.name, (error) ->
return done(error) if error?
console.info("Plugin installed: #{params.name}")
return done()
exports.update =
signature: 'plugin update <name>'
description: 'update a plugin'
help: '''
Use this command to update a resin plugin
Use `--quiet` to prevent information logging.
Examples:
$ resin plugin update hello
'''
permission: 'user'
action: (params, options, done) ->
plugins.update params.name, (error, version) ->
return done(error) if error?
console.info("Plugin updated: #{params.name}@#{version}")
return done()
exports.remove =
signature: 'plugin rm <name>'
description: 'remove a plugin'
help: '''
Use this command to remove a resin.io plugin.
Notice this command asks for confirmation interactively.
You can avoid this by passing the `--yes` boolean option.
Examples:
$ resin plugin rm hello
$ resin plugin rm hello --yes
'''
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
visuals.patterns.remove 'plugin', options.yes, (callback) ->
plugins.remove(params.name, callback)
, (error) ->
return done(error) if error?
console.info("Plugin removed: #{params.name}")
return done()

View File

@ -1,21 +0,0 @@
open = require('open')
url = require('url')
settings = require('resin-settings-client')
exports.preferences =
signature: 'preferences'
description: 'open preferences form'
help: '''
Use this command to open the preferences form.
In the future, we will allow changing all preferences directly from the terminal.
For now, we open your default web browser and point it to the web based preferences form.
Examples:
$ resin preferences
'''
permission: 'user'
action: ->
absUrl = url.resolve(settings.get('remoteUrl'), '/preferences')
open(absUrl)

299
lib/actions/preload.coffee Normal file
View File

@ -0,0 +1,299 @@
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
dockerUtils = require('../utils/docker')
LATEST = 'latest'
allDeviceTypes = undefined
getDeviceTypes = ->
Bluebird = require('bluebird')
if allDeviceTypes != undefined
return Bluebird.resolve(allDeviceTypes)
resin = require('resin-sdk').fromSharedOptions()
resin.models.config.getDeviceTypes()
.tap (dt) ->
allDeviceTypes = dt
getDeviceTypesWithSameArch = (deviceTypeSlug) ->
_ = require('lodash')
getDeviceTypes()
.then (deviceTypes) ->
deviceType = _.find(deviceTypes, slug: deviceTypeSlug)
_(deviceTypes).filter(arch: deviceType.arch).map('slug').value()
getApplicationsWithSuccessfulBuilds = (deviceType) ->
preload = require('resin-preload')
resin = require('resin-sdk').fromSharedOptions()
getDeviceTypesWithSameArch(deviceType)
.then (deviceTypes) ->
resin.pine.get
resource: 'my_application'
options:
$filter:
device_type:
$in: deviceTypes
owns__release:
$any:
$alias: 'r'
$expr:
r:
status: 'success'
$expand: preload.applicationExpandOptions
$select: [ 'id', 'app_name', 'device_type', 'commit', 'should_track_latest_release' ]
$orderby: 'app_name asc'
selectApplication = (deviceType) ->
visuals = require('resin-cli-visuals')
form = require('resin-cli-form')
{ exitWithExpectedError } = require('../utils/patterns')
applicationInfoSpinner = new visuals.Spinner('Downloading list of applications and releases.')
applicationInfoSpinner.start()
getApplicationsWithSuccessfulBuilds(deviceType)
.then (applications) ->
applicationInfoSpinner.stop()
if applications.length == 0
exitWithExpectedError("You have no apps with successful releases for a '#{deviceType}' device type.")
form.ask
message: 'Select an application'
type: 'list'
choices: applications.map (app) ->
name: app.app_name
value: app
selectApplicationCommit = (releases) ->
form = require('resin-cli-form')
{ exitWithExpectedError } = require('../utils/patterns')
if releases.length == 0
exitWithExpectedError('This application has no successful releases.')
DEFAULT_CHOICE = { 'name': LATEST, 'value': LATEST }
choices = [ DEFAULT_CHOICE ].concat releases.map (release) ->
name: "#{release.end_timestamp} - #{release.commit}"
value: release.commit
return form.ask
message: 'Select a release'
type: 'list'
default: LATEST
choices: choices
offerToDisableAutomaticUpdates = (application, commit) ->
Promise = require('bluebird')
resin = require('resin-sdk').fromSharedOptions()
form = require('resin-cli-form')
if commit == LATEST or not application.should_track_latest_release
return Promise.resolve()
message = '''
This application is set to automatically update all devices to the latest available version.
This might be unexpected behaviour: with this enabled, the preloaded device will still
download and install the latest release once it is online.
Do you want to disable automatic updates for this application?
Warning: To re-enable this requires direct api calls,
see https://docs.resin.io/reference/api/resources/device/#set-device-to-release
'''
form.ask
message: message,
type: 'confirm'
.then (update) ->
if not update
return
resin.pine.patch
resource: 'application'
id: application.id
body:
should_track_latest_release: false
module.exports =
signature: 'preload <image>'
description: '(beta) preload an app on a disk image (or Edison zip archive)'
help: '''
Warning: "resin preload" requires Docker to be correctly installed in
your shell environment. For more information (including Windows support)
please check the README here: https://github.com/resin-io/resin-cli .
Use this command to preload an application to a local disk image (or
Edison zip archive) with a built release from Resin.io.
Examples:
$ resin preload resin.img --app 1234 --commit e1f2592fc6ee949e68756d4f4a48e49bff8d72a0 --splash-image some-image.png
$ resin preload resin.img
'''
permission: 'user'
primary: true
options: dockerUtils.appendConnectionOptions [
{
signature: 'app'
parameter: 'appId'
description: 'id of the application to preload'
alias: 'a'
}
{
signature: 'commit'
parameter: 'hash'
description: '''
the commit hash for a specific application release to preload, use "latest" to specify the latest release
(ignored if no appId is given)
'''
alias: 'c'
}
{
signature: 'splash-image'
parameter: 'splashImage.png'
description: 'path to a png image to replace the splash screen'
alias: 's'
}
{
signature: 'dont-check-arch'
boolean: true
description: 'Disables check for matching architecture in image and application'
}
{
signature: 'pin-device-to-release'
boolean: true
description: 'Pin the preloaded device to the preloaded release on provision'
alias: 'p'
}
]
action: (params, options, done) ->
_ = require('lodash')
Promise = require('bluebird')
resin = require('resin-sdk').fromSharedOptions()
preload = require('resin-preload')
visuals = require('resin-cli-visuals')
nodeCleanup = require('node-cleanup')
{ exitWithExpectedError } = require('../utils/patterns')
progressBars = {}
progressHandler = (event) ->
progressBar = progressBars[event.name]
if not progressBar
progressBar = progressBars[event.name] = new visuals.Progress(event.name)
progressBar.update(percentage: event.percentage)
spinners = {}
spinnerHandler = (event) ->
spinner = spinners[event.name]
if not spinner
spinner = spinners[event.name] = new visuals.Spinner(event.name)
if event.action == 'start'
spinner.start()
else
console.log()
spinner.stop()
options.image = params.image
options.appId = options.app
delete options.app
options.splashImage = options['splash-image']
delete options['splash-image']
options.dontCheckArch = options['dont-check-arch'] || false
delete options['dont-check-arch']
if options.dontCheckArch and not options.appId
exitWithExpectedError('You need to specify an app id if you disable the architecture check.')
options.pinDevice = options['pin-device-to-release'] || false
delete options['pin-device-to-release']
# Get a configured dockerode instance
dockerUtils.getDocker(options)
.then (docker) ->
preloader = new preload.Preloader(
resin
docker
options.appId
options.commit
options.image
options.splashImage
options.proxy
options.dontCheckArch
options.pinDevice
)
gotSignal = false
nodeCleanup (exitCode, signal) ->
if signal
gotSignal = true
nodeCleanup.uninstall() # don't call cleanup handler again
preloader.cleanup()
.then ->
# calling process.exit() won't inform parent process of signal
process.kill(process.pid, signal)
return false
if process.env.DEBUG
preloader.stderr.pipe(process.stderr)
preloader.on('progress', progressHandler)
preloader.on('spinner', spinnerHandler)
return new Promise (resolve, reject) ->
preloader.on('error', reject)
preloader.prepare()
.then ->
# If no appId was provided, show a list of matching apps
Promise.try ->
if not preloader.appId
selectApplication(preloader.config.deviceType)
.then (application) ->
preloader.setApplication(application)
.then ->
# Use the commit given as --commit or show an interactive commit selection menu
Promise.try ->
if options.commit
if options.commit == LATEST and preloader.application.commit
# handle `--commit latest`
return LATEST
release = _.find preloader.application.owns__release, (release) ->
release.commit.startsWith(options.commit)
if not release
exitWithExpectedError('There is no release matching this commit')
return release.commit
selectApplicationCommit(preloader.application.owns__release)
.then (commit) ->
if commit == LATEST
preloader.commit = preloader.application.commit
else
preloader.commit = commit
# Propose to disable automatic app updates if the commit is not the latest
offerToDisableAutomaticUpdates(preloader.application, commit)
.then ->
# All options are ready: preload the image.
preloader.preload()
.catch(resin.errors.ResinError, exitWithExpectedError)
.then(resolve)
.catch(reject)
.then(done)
.finally ->
if not gotSignal
preloader.cleanup()

229
lib/actions/push.ts Normal file
View File

@ -0,0 +1,229 @@
/*
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { CommandDefinition } from 'capitano';
import { stripIndent } from 'common-tags';
import { ResinSDK } from 'resin-sdk';
import { BuildError } from '../utils/device/errors';
// An regex to detect an IP address, from https://www.regular-expressions.info/ip.html
const IP_REGEX = new RegExp(
/\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b/,
);
enum BuildTarget {
Cloud,
Device,
}
function getBuildTarget(appOrDevice: string): BuildTarget | null {
// First try the application regex from the api
if (/^[a-zA-Z0-9_-]+$/.test(appOrDevice)) {
return BuildTarget.Cloud;
}
if (IP_REGEX.test(appOrDevice)) {
return BuildTarget.Device;
}
return null;
}
async function getAppOwner(sdk: ResinSDK, appName: string) {
const {
exitWithExpectedError,
selectFromList,
} = await import('../utils/patterns');
const _ = await import('lodash');
const applications = await sdk.models.application.getAll({
$expand: {
user: {
$select: ['username'],
},
},
$filter: {
app_name: appName,
},
$select: ['id'],
});
if (applications == null || applications.length === 0) {
exitWithExpectedError(
stripIndent`
No applications found with name: ${appName}.
This could mean that the application does not exist, or you do
not have the permissions required to access it.`,
);
}
if (applications.length === 1) {
return _.get(applications, '[0].user[0].username');
}
// If we got more than one application with the same name it means that the
// user has access to a collab app with the same name as a personal app. We
// present a list to the user which shows the fully qualified application
// name (user/appname) and allows them to select
const entries = _.map(applications, app => {
const username = _.get(app, 'user[0].username');
return {
name: `${username}/${appName}`,
extra: username,
};
});
const selected = await selectFromList(
`${
entries.length
} applications found with that name, please select the application you would like to push to`,
entries,
);
return selected.extra;
}
export const push: CommandDefinition<
{
applicationOrDevice: string;
},
{
source: string;
emulated: boolean;
nocache: boolean;
}
> = {
signature: 'push <applicationOrDevice>',
description:
'Start a remote build on the resin.io cloud build servers or a local mode device',
help: stripIndent`
This command can be used to start a build on the remote
resin.io cloud builders, or a local mode resin device.
When building on the resin cloud the given source directory will be sent to the
resin.io builder, and the build will proceed. This can be used as a drop-in
replacement for git push to deploy.
When building on a local mode device, the given source directory will be built on
device, and the resulting containers will be run on the device. Logs will be
streamed back from the device as part of the same invocation.
Examples:
$ resin push myApp
$ resin push myApp --source <source directory>
$ resin push myApp -s <source directory>
$ resin push 10.0.0.1
$ resin push 10.0.0.1 --source <source directory>
$ resin push 10.0.0.1 -s <source directory>
`,
permission: 'user',
options: [
{
signature: 'source',
alias: 's',
description:
'The source that should be sent to the resin builder to be built (defaults to the current directory)',
parameter: 'source',
},
{
signature: 'emulated',
alias: 'e',
description: 'Force an emulated build to occur on the remote builder',
boolean: true,
},
{
signature: 'nocache',
alias: 'c',
description: "Don't use cache when building this project",
boolean: true,
},
],
async action(params, options, done) {
const sdk = (await import('resin-sdk')).fromSharedOptions();
const Bluebird = await import('bluebird');
const remote = await import('../utils/remote-build');
const deviceDeploy = await import('../utils/device/deploy');
const { exitWithExpectedError } = await import('../utils/patterns');
const appOrDevice: string | null = params.applicationOrDevice;
if (appOrDevice == null) {
exitWithExpectedError('You must specify an application or a device');
}
const source = options.source || '.';
if (process.env.DEBUG) {
console.log(`[debug] Using ${source} as build source`);
}
const buildTarget = getBuildTarget(appOrDevice);
switch (buildTarget) {
case BuildTarget.Cloud:
const app = appOrDevice;
Bluebird.join(
sdk.auth.getToken(),
sdk.settings.get('resinUrl'),
getAppOwner(sdk, app),
(token, baseUrl, owner) => {
const opts = {
emulated: options.emulated,
nocache: options.nocache,
};
const args = {
app,
owner,
source,
auth: token,
baseUrl,
sdk,
opts,
};
return remote.startRemoteBuild(args);
},
).nodeify(done);
break;
case BuildTarget.Device:
const device = appOrDevice;
// TODO: Support passing a different port
Bluebird.resolve(
deviceDeploy.deployToDevice({
source,
deviceHost: device,
}),
)
.catch(BuildError, e => {
exitWithExpectedError(e.toString());
})
.nodeify(done);
break;
default:
exitWithExpectedError(
stripIndent`
Build target not recognised. Please provide either an application name or device address.
The only supported device addresses currently are IP addresses.
If you believe your build target should have been detected, and this is an error, please
create an issue.`,
);
break;
}
},
};

39
lib/actions/settings.ts Normal file
View File

@ -0,0 +1,39 @@
/*
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { CommandDefinition } from 'capitano';
export const list: CommandDefinition = {
signature: 'settings',
description: 'print current settings',
help: `\
Use this command to display detected settings
Examples:
$ resin settings\
`,
async action(_params, _options, done) {
const resin = (await import('resin-sdk')).fromSharedOptions();
const prettyjson = await import('prettyjson');
return resin.settings
.getAll()
.then(prettyjson.render)
.then(console.log)
.nodeify(done);
},
};

141
lib/actions/ssh.coffee Normal file
View File

@ -0,0 +1,141 @@
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
commandOptions = require('./command-options')
{ normalizeUuidProp } = require('../utils/normalization')
module.exports =
signature: 'ssh [uuid]'
description: '(beta) get a shell into the running app container of a device'
help: '''
Warning: 'resin ssh' requires an openssh-compatible client to be correctly
installed in your shell environment. For more information (including Windows
support) please check the README here: https://github.com/resin-io/resin-cli
Use this command to get a shell into the running application container of
your device.
Examples:
$ resin ssh MyApp
$ resin ssh 7cf02a6
$ resin ssh 7cf02a6 --port 8080
$ resin ssh 7cf02a6 -v
$ resin ssh 7cf02a6 -s
'''
permission: 'user'
primary: true
options: [
signature: 'port'
parameter: 'port'
description: 'ssh gateway port'
alias: 'p'
,
signature: 'verbose'
boolean: true
description: 'increase verbosity'
alias: 'v'
commandOptions.hostOSAccess,
signature: 'noproxy'
boolean: true
description: "don't use the proxy configuration for this connection.
Only makes sense if you've configured proxy globally."
]
action: (params, options, done) ->
normalizeUuidProp(params)
child_process = require('child_process')
Promise = require('bluebird')
resin = require('resin-sdk-preconfigured')
_ = require('lodash')
bash = require('bash')
hasbin = require('hasbin')
{ getSubShellCommand } = require('../utils/helpers')
patterns = require('../utils/patterns')
options.port ?= 22
verbose = if options.verbose then '-vvv' else ''
proxyConfig = global.PROXY_CONFIG
useProxy = !!proxyConfig and not options.noproxy
getSshProxyCommand = (hasTunnelBin) ->
return '' if not useProxy
if not hasTunnelBin
console.warn('''
Proxy is enabled but the `proxytunnel` binary cannot be found.
Please install it if you want to route the `resin ssh` requests through the proxy.
Alternatively you can pass `--noproxy` param to the `resin ssh` command to ignore the proxy config
for the `ssh` requests.
Attemmpting the unproxied request for now.
''')
return ''
tunnelOptions =
proxy: "#{proxyConfig.host}:#{proxyConfig.port}"
dest: '%h:%p'
{ proxyAuth } = proxyConfig
if proxyAuth
i = proxyAuth.indexOf(':')
_.assign tunnelOptions,
user: proxyAuth.substring(0, i)
pass: proxyAuth.substring(i + 1)
proxyCommand = "proxytunnel #{bash.args(tunnelOptions, '--', '=')}"
return "-o #{bash.args({ ProxyCommand: proxyCommand }, '', '=')}"
Promise.try ->
return false if not params.uuid
return resin.models.device.has(params.uuid)
.then (uuidExists) ->
return params.uuid if uuidExists
return patterns.inferOrSelectDevice()
.then (uuid) ->
console.info("Connecting to: #{uuid}")
resin.models.device.get(uuid)
.then (device) ->
patterns.exitWithExpectedError('Device is not online') if not device.is_online
Promise.props
username: resin.auth.whoami()
uuid: device.uuid
# get full uuid
containerId: if options.host then '' else resin.models.device.getApplicationInfo(device.uuid).get('containerId')
proxyUrl: resin.settings.get('proxyUrl')
hasTunnelBin: if useProxy then hasbin('proxytunnel') else null
.then ({ username, uuid, containerId, proxyUrl, hasTunnelBin }) ->
throw new Error('Did not find running application container') if not containerId?
Promise.try ->
sshProxyCommand = getSshProxyCommand(hasTunnelBin)
if options.host
accessCommand = "host #{uuid}"
else
accessCommand = "enter #{uuid} #{containerId}"
command = "ssh #{verbose} -t \
-o LogLevel=ERROR \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
#{sshProxyCommand} \
-p #{options.port} #{username}@ssh.#{proxyUrl} #{accessCommand}"
subShellCommand = getSubShellCommand(command)
child_process.spawn subShellCommand.program, subShellCommand.args,
stdio: 'inherit'
.nodeify(done)

18
lib/actions/sync.ts Normal file
View File

@ -0,0 +1,18 @@
/*
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as ResinSync from 'resin-sync';
export = ResinSync.capitano('resin-cli');

View File

@ -1,58 +0,0 @@
_ = require('lodash')
child_process = require('child_process')
president = require('president')
npm = require('../npm')
packageJSON = require('../../package.json')
exports.update =
signature: 'update'
description: 'update the resin cli'
help: '''
Use this command to update the Resin CLI
This command outputs information about the update process.
Use `--quiet` to remove that output.
The Resin CLI checks for updates once per day.
Major updates require a manual update with this update command,
while minor updates are applied automatically.
Examples:
$ resin update
'''
action: (params, options, done) ->
npm.isUpdated packageJSON.name, packageJSON.version, (error, isUpdated) ->
return done(error) if error?
if isUpdated
return done(new Error('You\'re already running the latest version.'))
onUpdate = (error, stdout, stderr) ->
return done(error) if error?
return done(new Error(stderr)) if not _.isEmpty(stderr)
console.info("Upgraded #{packageJSON.name}.")
return done()
command = "npm install --global #{packageJSON.name}"
# Attempting to self update using the NPM API was not considered safe.
# A safer thing to do is to call npm as a child process
# https://github.com/npm/npm/issues/7723
child_process.exec command, (error, stdout, stderr) ->
return onUpdate(null, stdout, stderr) if error?
if _.any [
# 3 is equivalent to EACCES for NPM
error.code is 3
error.code is 'EPERM'
error.code is 'ACCES'
]
return president.execute(command, onUpdate)
return done(error)

56
lib/actions/util.coffee Normal file
View File

@ -0,0 +1,56 @@
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
_ = require('lodash')
exports.availableDrives =
# TODO: dedupe with https://github.com/resin-io-modules/resin-cli-visuals/blob/master/lib/widgets/drive/index.coffee
signature: 'util available-drives'
description: 'list available drives'
help: """
Use this command to list your machine's drives usable for writing the OS image to.
Skips the system drives.
"""
action: ->
Promise = require('bluebird')
drivelist = require('drivelist')
driveListAsync = Promise.promisify(drivelist.list)
chalk = require('chalk')
visuals = require('resin-cli-visuals')
formatDrive = (drive) ->
size = drive.size / 1000000000
return {
device: drive.device
size: "#{size.toFixed(1)} GB"
description: drive.description
}
getDrives = ->
driveListAsync().then (drives) ->
return _.reject(drives, system: true)
getDrives()
.then (drives) ->
if not drives.length
console.error("#{chalk.red('x')} No available drives were detected, plug one in!")
return
console.log visuals.table.horizontal drives.map(formatDrive), [
'device'
'size'
'description'
]

74
lib/actions/wizard.coffee Normal file
View File

@ -0,0 +1,74 @@
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
exports.wizard =
signature: 'quickstart [name]'
description: 'getting started with resin.io'
help: '''
Use this command to run a friendly wizard to get started with resin.io.
The wizard will guide you through:
- Create an application.
- Initialise an SDCard with the resin.io operating system.
- Associate an existing project directory with your resin.io application.
- Push your project to your devices.
Examples:
$ resin quickstart
$ resin quickstart MyApp
'''
primary: true
action: (params, options, done) ->
resin = require('resin-sdk').fromSharedOptions()
patterns = require('../utils/patterns')
{ runCommand } = require('../utils/helpers')
resin.auth.isLoggedIn().then (isLoggedIn) ->
return if isLoggedIn
console.info('Looks like you\'re not logged in yet!')
console.info("Let's go through a quick wizard to get you started.\n")
return runCommand('login')
.then ->
return if params.name?
patterns.selectOrCreateApplication().tap (applicationName) ->
resin.models.application.has(applicationName).then (hasApplication) ->
return applicationName if hasApplication
runCommand("app create #{applicationName}")
.then (applicationName) ->
params.name = applicationName
.then ->
return runCommand("device init --application #{params.name}")
.tap(patterns.awaitDevice)
.then (uuid) ->
return runCommand("device #{uuid}")
.then ->
return resin.models.application.get(params.name)
.then (application) ->
console.log """
Your device is ready to start pushing some code!
Check our official documentation for more information:
http://docs.resin.io/#/pages/introduction/introduction.md
Clone an example or go to an existing application directory and run:
$ git remote add resin #{application.git_repository}
$ git push resin master
"""
.nodeify(done)

View File

@ -1,43 +1,111 @@
_ = require('lodash')
async = require('async')
###
Copyright 2016-2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
Raven = require('raven')
Raven.disableConsoleAlerts()
Raven.config require('./config').sentryDsn,
captureUnhandledRejections: true,
autoBreadcrumbs: true,
release: require('../package.json').version
.install (logged, error) ->
console.error(error)
process.exit(1)
Raven.setContext
extra:
args: process.argv
node_version: process.version
validNodeVersions = require('../package.json').engines.node
if not require('semver').satisfies(process.version, validNodeVersions)
console.warn """
Warning: this version of Node does not match the requirements of this package.
This package expects #{validNodeVersions}, but you're using #{process.version}.
This may cause unexpected behaviour.
To upgrade your Node, visit https://nodejs.org/en/download/
"""
# Doing this before requiring any other modules,
# including the 'resin-sdk', to prevent any module from reading the http proxy config
# before us
globalTunnel = require('global-tunnel-ng')
settings = require('resin-settings-client')
try
proxy = settings.get('proxy') or null
catch
proxy = null
# Init the tunnel even if the proxy is not configured
# because it can also get the proxy from the http(s)_proxy env var
# If that is not set as well the initialize will do nothing
globalTunnel.initialize(proxy)
# TODO: make this a feature of capitano https://github.com/resin-io/capitano/issues/48
global.PROXY_CONFIG = globalTunnel.proxyConfig
Promise = require('bluebird')
capitano = require('capitano')
resin = require('resin-sdk')
capitanoExecuteAsync = Promise.promisify(capitano.execute)
# We don't yet use resin-sdk directly everywhere, but we set up shared
# options correctly so we can do safely in submodules
ResinSdk = require('resin-sdk')
ResinSdk.setSharedOptions(
apiUrl: settings.get('apiUrl')
imageMakerUrl: settings.get('imageMakerUrl')
dataDirectory: settings.get('dataDirectory')
retries: 2
)
resin = ResinSdk.fromSharedOptions()
actions = require('./actions')
errors = require('./errors')
plugins = require('./plugins')
update = require('./update')
events = require('./events')
update = require('./utils/update')
{ exitWithExpectedError } = require('./utils/patterns')
# Assign bluebird as the global promise library
# stream-to-promise will produce native promises if not
# for this module, which could wreak havoc in this
# bluebird-only codebase.
require('any-promise/register/bluebird')
capitano.permission 'user', (done) ->
resin.auth.isLoggedIn (isLoggedIn) ->
resin.auth.isLoggedIn().then (isLoggedIn) ->
if not isLoggedIn
return done(new Error('You have to log in'))
return done()
exitWithExpectedError('''
You have to log in to continue
Run the following command to go through the login wizard:
$ resin login
''')
.nodeify(done)
capitano.command
signature: '*'
action: ->
capitano.execute(command: 'help')
# ---------- Options ----------
capitano.globalOption
signature: 'quiet'
description: 'quiet (no output)'
boolean: true
alias: 'q'
capitano.globalOption
signature: 'project'
parameter: 'path'
description: 'project path'
alias: 'j'
# We don't do anything in response to this options
# explicitly. We use InquirerJS to provide CLI widgets,
# and that module understands --no-color automatically.
capitano.globalOption
signature: 'no-color'
description: 'disable colour highlighting'
signature: 'help'
boolean: true
alias: 'h'
# ---------- Info Module ----------
capitano.command(actions.info.version)
@ -45,39 +113,45 @@ capitano.command(actions.info.version)
# ---------- Help Module ----------
capitano.command(actions.help.help)
# ---------- Wizard Module ----------
capitano.command(actions.wizard.wizard)
# ---------- Api key module ----------
capitano.command(actions.apiKey.generate)
# ---------- App Module ----------
capitano.command(actions.app.create)
capitano.command(actions.app.list)
capitano.command(actions.app.remove)
capitano.command(actions.app.restart)
capitano.command(actions.app.info)
# ---------- Auth Module ----------
capitano.command(actions.auth.login)
capitano.command(actions.auth.logout)
capitano.command(actions.auth.signup)
capitano.command(actions.auth.whoami)
# ---------- App Module ----------
capitano.command(actions.app.create)
capitano.command(actions.app.list)
capitano.command(actions.app.info)
capitano.command(actions.app.remove)
capitano.command(actions.app.restart)
capitano.command(actions.app.associate)
capitano.command(actions.app.init)
# ---------- Device Module ----------
capitano.command(actions.device.list)
capitano.command(actions.device.supported)
capitano.command(actions.device.rename)
capitano.command(actions.device.init)
capitano.command(actions.device.info)
capitano.command(actions.device.remove)
capitano.command(actions.device.identify)
# ---------- Drive Module ----------
capitano.command(actions.drive.list)
capitano.command(actions.device.reboot)
capitano.command(actions.device.shutdown)
capitano.command(actions.device.enableDeviceUrl)
capitano.command(actions.device.disableDeviceUrl)
capitano.command(actions.device.getDeviceUrl)
capitano.command(actions.device.hasDeviceUrl)
capitano.command(actions.device.register)
capitano.command(actions.device.move)
capitano.command(actions.device.info)
# ---------- Notes Module ----------
capitano.command(actions.notes.set)
# ---------- Preferences Module ----------
capitano.command(actions.preferences.preferences)
# ---------- Keys Module ----------
capitano.command(actions.keys.list)
capitano.command(actions.keys.add)
@ -90,50 +164,71 @@ capitano.command(actions.env.add)
capitano.command(actions.env.rename)
capitano.command(actions.env.remove)
# ---------- Logs Module ----------
capitano.command(actions.logs.logs)
# ---------- OS Module ----------
capitano.command(actions.os.versions)
capitano.command(actions.os.download)
capitano.command(actions.os.install)
capitano.command(actions.os.buildConfig)
capitano.command(actions.os.configure)
capitano.command(actions.os.initialize)
# ---------- Examples Module ----------
capitano.command(actions.examples.list)
capitano.command(actions.examples.clone)
capitano.command(actions.examples.info)
# ---------- Config Module ----------
capitano.command(actions.config.read)
capitano.command(actions.config.write)
capitano.command(actions.config.inject)
capitano.command(actions.config.reconfigure)
capitano.command(actions.config.generate)
# ---------- Plugins Module ----------
capitano.command(actions.plugin.list)
capitano.command(actions.plugin.install)
capitano.command(actions.plugin.update)
capitano.command(actions.plugin.remove)
# ---------- Settings Module ----------
capitano.command(actions.settings.list)
# ---------- Update Module ----------
capitano.command(actions.update.update)
# ---------- Logs Module ----------
capitano.command(actions.logs)
changeProjectDirectory = (directory) ->
try
process.chdir(directory)
catch
errors.handle(new Error("Invalid project: #{directory}"))
# ---------- Sync Module ----------
capitano.command(actions.sync)
async.waterfall([
# ---------- Preload Module ----------
capitano.command(actions.preload)
(callback) ->
update.check(callback)
# ---------- SSH Module ----------
capitano.command(actions.ssh)
(callback) ->
plugins.register('resin-plugin-', callback)
# ---------- Local ResinOS Module ----------
capitano.command(actions.local.configure)
capitano.command(actions.local.flash)
capitano.command(actions.local.logs)
capitano.command(actions.local.push)
capitano.command(actions.local.ssh)
capitano.command(actions.local.scan)
capitano.command(actions.local.stop)
(callback) ->
cli = capitano.parse(process.argv)
# ---------- Public utils ----------
capitano.command(actions.util.availableDrives)
if cli.global.quiet
console.info = _.noop
# ---------- Internal utils ----------
capitano.command(actions.internal.osInit)
capitano.command(actions.internal.scanDevices)
capitano.command(actions.internal.sudo)
if cli.global.project?
changeProjectDirectory(cli.global.project)
#------------ Local build and deploy -------
capitano.command(actions.build)
capitano.command(actions.deploy)
capitano.execute(cli, callback)
#------------ Push/remote builds -------
capitano.command(actions.push.push)
], errors.handle)
#------------ Join/Leave -------
capitano.command(actions.join.join)
capitano.command(actions.leave.leave)
update.notify()
cli = capitano.parse(process.argv)
runCommand = ->
if cli.global?.help
capitanoExecuteAsync(command: "help #{cli.command ? ''}")
else
capitanoExecuteAsync(cli)
Promise.all([events.trackCommand(cli), runCommand()])
.catch(errors.handle)

63
lib/auth/index.coffee Normal file
View File

@ -0,0 +1,63 @@
###
Copyright 2016 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
###*
# @module auth
###
open = require('opn')
resin = require('resin-sdk').fromSharedOptions()
server = require('./server')
utils = require('./utils')
###*
# @summary Login to the Resin CLI using the web dashboard
# @function
# @public
#
# @description
# This function opens the user's default browser and points it
# to the Resin.io dashboard where the session token exchange will
# take place.
#
# Once the the token is retrieved, it's automatically persisted.
#
# @fulfil {String} - session token
# @returns {Promise}
#
# @example
# auth.login().then (sessionToken) ->
# console.log('I\'m logged in!')
# console.log("My session token is: #{sessionToken}")
###
exports.login = ->
options =
port: 8989
path: '/auth'
# Needs to be 127.0.0.1 not localhost, because the ip only is whitelisted
# from mixed content warnings (as the target of a form in the result page)
callbackUrl = "http://127.0.0.1:#{options.port}#{options.path}"
return utils.getDashboardLoginURL(callbackUrl).then (loginUrl) ->
# Leave a bit of time for the
# server to get up and runing
setTimeout ->
open(loginUrl, { wait: false })
, 1000
return server.awaitForToken(options)
.tap(resin.auth.loginWithToken)

21
lib/auth/pages/error.ejs Normal file
View File

@ -0,0 +1,21 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>Resin CLI - Error</title>
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="./static/style.css" inline>
</head>
<body>
<div class="center">
<img class="icon" src="./static/images/sad.png" inline>
<h1>Something went wrong</h1>
<p>You couldn't login to the Resin CLI for some reason</p>
<br>
<br>
<a href="https://forums.resin.io/" class="button danger">Get help in our forums</a>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Some files were not shown because too many files have changed in this diff Show More