mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-06-24 18:45:07 +00:00
Compare commits
1054 Commits
Author | SHA1 | Date | |
---|---|---|---|
8fa6ad0735 | |||
db32d89f1f | |||
bb2365b3fd | |||
a9db392580 | |||
d86fcfcda4 | |||
7fb03b8511 | |||
3b52f7ba4e | |||
69396904c6 | |||
71e8448fbf | |||
41ff793372 | |||
e4432d1a90 | |||
bd6cb04a2b | |||
001c8f9601 | |||
f106b95be2 | |||
6fbe602b77 | |||
dc549a665b | |||
46ca62db3e | |||
eb68bb1a1a | |||
93d1e3a4a1 | |||
ff2ee59dae | |||
6217b4a6b5 | |||
67fcc6791c | |||
49d78c56fa | |||
e38a0c0047 | |||
eef0d9cdbe | |||
08c40195e5 | |||
7e306fbce8 | |||
656656bec1 | |||
87f46cb957 | |||
f7075d7db9 | |||
2a2d621d6a | |||
2db6cdd063 | |||
1fafe64579 | |||
6562eb544c | |||
3763bf0712 | |||
890a02e2c8 | |||
427664c729 | |||
727a245715 | |||
a2635f47ee | |||
d316f67367 | |||
ebd1d7e370 | |||
eef192ff68 | |||
36d3c92006 | |||
68e31468cc | |||
dfd8b6717d | |||
10d688c02d | |||
737e961979 | |||
3bca36c277 | |||
24b2c83e92 | |||
266870018a | |||
80bc044415 | |||
563628a5a9 | |||
385e2c7f7a | |||
19ce4c4cdb | |||
0b26fda89c | |||
3f692ecbb0 | |||
2d43e47610 | |||
a8f1d16b26 | |||
8e95757f47 | |||
3fd4f328ab | |||
97eaf174ec | |||
2ef56a9a3f | |||
93818b1a98 | |||
ce70102378 | |||
0e4c6c459c | |||
6b96fe37ba | |||
e9c7e0e924 | |||
119fa78927 | |||
dad655c9ec | |||
8af392029f | |||
82888de036 | |||
b4a56e1541 | |||
19b0ec7f8b | |||
3df7bfe700 | |||
d1fd5a6bff | |||
43a7e3ddf4 | |||
10976bed43 | |||
c187c113d9 | |||
f7c0258145 | |||
eb729d149e | |||
39ac28d4ef | |||
989877d541 | |||
3f3af216fd | |||
9aef632afd | |||
ef6e00bcea | |||
55a7dccc74 | |||
62035fac83 | |||
950201973b | |||
e431083e84 | |||
1ff9cf02d7 | |||
bd3b9e32a9 | |||
5a620d6c9e | |||
492e35e5c2 | |||
8980bc704a | |||
8b9e78d645 | |||
8f0131cf50 | |||
47407a84fb | |||
d858f3fd90 | |||
a36f765f1b | |||
fd308a5131 | |||
3052100973 | |||
cfdd4d3d69 | |||
0a3123a9cf | |||
5474666f9e | |||
2bbd45e867 | |||
e8c4a9abfd | |||
710a938b3f | |||
be7c1d278e | |||
dfcac4a532 | |||
a5128cd49e | |||
223432406d | |||
cafde01886 | |||
0158d1da48 | |||
e0d661a1da | |||
b152482133 | |||
4cdf3acf42 | |||
314fcd3919 | |||
b07a394592 | |||
14c5d938a6 | |||
c6c2f0bedc | |||
6882f4bbe8 | |||
74d6cfb8d2 | |||
3b30a7c4b0 | |||
115e46573b | |||
2ad789457f | |||
9beb6de7d8 | |||
74743745c4 | |||
6fae5a2dd9 | |||
4320f33d8e | |||
435fedfa07 | |||
999f269e36 | |||
224dfce4a8 | |||
60634a5ebd | |||
f8f1f52662 | |||
e204707ee0 | |||
b28a4a5f99 | |||
340b2d5572 | |||
4dec846256 | |||
dc1b3c3239 | |||
2fecb8d3e9 | |||
4665a72baf | |||
b208d601c9 | |||
22b3c39b2b | |||
30cca93283 | |||
f0d9b615a7 | |||
b7e2c2571f | |||
25538a9afb | |||
5daa682183 | |||
bad4493867 | |||
9e6dd57a5c | |||
1b86741fa2 | |||
6cca43a09e | |||
b622995a5a | |||
4329857a16 | |||
d803cfab3a | |||
e7d7ca807f | |||
22e0b4b9dc | |||
759baf3eda | |||
e6dc7f8075 | |||
1f0bec39d9 | |||
64b6549fde | |||
fc7e08c886 | |||
4aadfe9326 | |||
a65868cbbf | |||
8ab528aae4 | |||
d93b82a269 | |||
3095938b0e | |||
91b3442fc9 | |||
e660c6ae90 | |||
e2a165ce80 | |||
ce5685551d | |||
15e677e9f1 | |||
5ccde3db8e | |||
cb550f65bb | |||
8d3987fc70 | |||
4fa8d86f02 | |||
6b22166887 | |||
9e555b3dba | |||
bea7b2035a | |||
abebf6b4f4 | |||
6182e7c98a | |||
410390a9ae | |||
11079caf26 | |||
0c6545218a | |||
7fc806c75f | |||
18533de3da | |||
c5afe5f2c2 | |||
2875bd672e | |||
26d123b33d | |||
5000febf72 | |||
378f894da3 | |||
c891d690ec | |||
8efaec63ff | |||
b756f2a597 | |||
c986e142e2 | |||
8bdacbb11e | |||
d2a9aee685 | |||
77a4c6fdc2 | |||
4d935d62fc | |||
e8b44d7250 | |||
5c5cfde49f | |||
97480d3aa4 | |||
4ac8cb1003 | |||
2e7e033bb9 | |||
9fb5b52069 | |||
ad940824a6 | |||
ed83514a2f | |||
8d91a5732a | |||
1cfe64e4a7 | |||
24388811ad | |||
f465e74a87 | |||
c8d51d92e7 | |||
5a28d4c92f | |||
66a4faeea5 | |||
2fce0e964b | |||
a29b40eefa | |||
cf7bf2cb7d | |||
df3c5ca07f | |||
e584dc43f7 | |||
90a5b15dbc | |||
390332cd6c | |||
37ec11bf25 | |||
08ce1abd20 | |||
f2862f7fe2 | |||
e0673c98fc | |||
4ab67ed71d | |||
5ea263ef2e | |||
31419b399e | |||
9e8b09010f | |||
974be5cc13 | |||
ec386b807f | |||
abc183a729 | |||
e5ed6fab85 | |||
eb9152255e | |||
78ab47b584 | |||
5b651c7821 | |||
95fc5d7785 | |||
4d18e92686 | |||
2d729a82a0 | |||
4b5240d8cd | |||
6ae59654a0 | |||
b88f7a993c | |||
880fb43fd9 | |||
fa71df7c70 | |||
bc79832e1d | |||
1d06bc1b4f | |||
88d5ec0c94 | |||
ebd9e92b10 | |||
a5b535753f | |||
6e5e4bd8a6 | |||
6e034acf23 | |||
3a44782c38 | |||
654ec75598 | |||
66876a2c85 | |||
8cb862359b | |||
172fa37bd4 | |||
fc5640c79d | |||
db9225f00a | |||
c12b59b978 | |||
7eeafa935a | |||
404348f92e | |||
4d2af251b2 | |||
d249ac168a | |||
65eaad2ed5 | |||
3ff5880ae3 | |||
511d2abe1d | |||
029b7c7164 | |||
afafa22694 | |||
2df4422748 | |||
e099f0b8b3 | |||
8866f47805 | |||
1d8382e91d | |||
110e18bc88 | |||
3df30c8b5a | |||
ebfd842ae5 | |||
39b171fd2a | |||
04c2333a54 | |||
65884c81a4 | |||
f50ae65560 | |||
be0d6d5a99 | |||
4fa1a9c1c6 | |||
284030d83d | |||
9050cb1975 | |||
d55ce853a7 | |||
d3772386bf | |||
2e042499af | |||
225d3acf9e | |||
75d10286ad | |||
d6e0616c58 | |||
380a94f0f8 | |||
9da8c5f09a | |||
89b88315e4 | |||
11e8ca178c | |||
e583e6f04c | |||
0cce2a7ab7 | |||
965aa7e4d4 | |||
0ca64b18a2 | |||
63e1313f44 | |||
96a7a24738 | |||
dede4bb329 | |||
77b30409bb | |||
137473353c | |||
08b3db717e | |||
6cf32e445a | |||
857c5204b9 | |||
169609620a | |||
8149172eb0 | |||
cba105a41b | |||
69dff0c603 | |||
6ffa4070ff | |||
f05b04a6a1 | |||
88d8112402 | |||
f940d7428c | |||
697779868e | |||
d90874dbef | |||
65b2347b57 | |||
b25034978b | |||
a817bb2135 | |||
b629c3601e | |||
3619b2f117 | |||
4231f50c4c | |||
95fff4b7c4 | |||
b3aa3d35f7 | |||
9f3108c5e7 | |||
c95a01e34e | |||
19c51929a9 | |||
73dd625ede | |||
08db3ace03 | |||
2125cf9649 | |||
e5a7fa5617 | |||
3324ff4dee | |||
7ad468dc54 | |||
8474ee726c | |||
29b2f4afa8 | |||
7aee4d6d7f | |||
7af004501a | |||
2d09c18d6b | |||
53bf314820 | |||
1ae1a15259 | |||
20ed8c9169 | |||
977e3fb0ff | |||
c5df32f952 | |||
f5cd3375f2 | |||
3b4c8f2a01 | |||
356042557e | |||
00753a5776 | |||
eea9a2f723 | |||
5ebdf4a8ac | |||
fb06249b08 | |||
9214cb0d90 | |||
a04c3b9c7b | |||
2fde6241c2 | |||
ffa645f85c | |||
b629ee6164 | |||
7a4de5357e | |||
5bbb055cd9 | |||
553b96e48f | |||
7a0e8beb07 | |||
f17cbb1205 | |||
b690060bc4 | |||
72b2058d27 | |||
bbd617ea76 | |||
f9a4f8c375 | |||
5c289a6e00 | |||
3b439282ae | |||
d473509675 | |||
49664b815d | |||
06cc863f02 | |||
099cf997cb | |||
e8183d4031 | |||
6954da4a24 | |||
c18e8f1dbd | |||
741b53ad7e | |||
8282785b2a | |||
e013986dba | |||
3083ff0640 | |||
01cad3c048 | |||
c9919a90a8 | |||
0bf6bcc430 | |||
9cf42462c0 | |||
0f4eca2ff0 | |||
d414d1c5e3 | |||
afe98ff37d | |||
ce026ea387 | |||
25f8f3d740 | |||
f719f5c948 | |||
e2cb79edb1 | |||
c6e669fa6b | |||
7953f6f690 | |||
baf367b276 | |||
7fecb53cdf | |||
b4edb7ed7f | |||
16a1741374 | |||
e0a2217b94 | |||
7bd8a683b2 | |||
6b00bbc73a | |||
42d0b52df7 | |||
97c768edcd | |||
10a0924cd7 | |||
fdb8bf6967 | |||
81d8974213 | |||
af8d20ea3f | |||
7d606568f6 | |||
de13337655 | |||
8b485b5ad5 | |||
fbccf8a465 | |||
ce50d8b73d | |||
2088cbe896 | |||
870ce974e0 | |||
5f8c261288 | |||
cb386d15aa | |||
5ed26250d2 | |||
7b0415a270 | |||
3adb8f19bd | |||
d81fbad6f3 | |||
a70e38ef12 | |||
aeba64b1ee | |||
e6e44474a4 | |||
ea44c0571b | |||
f68364b695 | |||
81a6843c93 | |||
b672ff1fa1 | |||
b4c89e812c | |||
68808e760e | |||
69c98f4afb | |||
509fef754e | |||
6d1d4dc173 | |||
0d7d6de7cd | |||
c5452f9304 | |||
12854db923 | |||
51f9e18f6a | |||
29c20e32f6 | |||
995194fe2c | |||
7c784909fd | |||
a90d568d5c | |||
a265063fa1 | |||
1c5945d3ae | |||
6062c7a306 | |||
b061644b19 | |||
38b97baf02 | |||
8315bcb7db | |||
e8fa76266f | |||
3db3baeb7c | |||
17550f9bc9 | |||
44f80f7a39 | |||
372fa01cf3 | |||
9a515ef4e3 | |||
6beb6ff44e | |||
c2cd10f6a7 | |||
4d3769def8 | |||
42bfb3b0cc | |||
d9c2717fe4 | |||
8e93577f90 | |||
5b7e1b656e | |||
4a05ce3f53 | |||
61574a8522 | |||
9400d4027a | |||
b5ec49dda1 | |||
1c66efb4fa | |||
68fca3d030 | |||
325304aebe | |||
a50cf5b198 | |||
1b7aeeafc1 | |||
6e3c2ef168 | |||
d9b4753690 | |||
ca40d7ca65 | |||
4aa8362be9 | |||
d68b61a913 | |||
20969ef249 | |||
469d35fcc1 | |||
e9b8c38eeb | |||
d4c44bf350 | |||
2d8cf7c479 | |||
ca6e715bfa | |||
3a839c947e | |||
9896de3c34 | |||
03d7520de2 | |||
4fc8b130f8 | |||
c5692f8b13 | |||
85561b5d50 | |||
88c0833ae2 | |||
7df78a0c9e | |||
30663b0301 | |||
4ffba0ed56 | |||
a522c70f92 | |||
f295837840 | |||
091ae8eb03 | |||
6405c6bb6f | |||
797122ce37 | |||
f81db5a775 | |||
0f84aea47d | |||
84ed20d3ec | |||
9870727e36 | |||
db3de2137b | |||
92c6af91ca | |||
d578f62dca | |||
d4527220c3 | |||
d5797124f5 | |||
654c3c627d | |||
3953b00e77 | |||
13f33da280 | |||
356d2ef6b2 | |||
36a8179e0c | |||
aedb9c732f | |||
c3bd433532 | |||
d72750de65 | |||
ccd8e73c4e | |||
6c677fe8cd | |||
eaa1a798c5 | |||
da1b446b3b | |||
18b4509fef | |||
35bba04b16 | |||
dd382158dd | |||
40f015de93 | |||
bee523828a | |||
474635401a | |||
a8c30bb395 | |||
9e00fdaf31 | |||
5f83c870ed | |||
5d89533afc | |||
a346c3f043 | |||
de0649c980 | |||
b7300deab7 | |||
70b2ba3ab9 | |||
8cbf792786 | |||
bca9a7f51f | |||
4bf079377b | |||
ebefd816b6 | |||
fb1ef0df63 | |||
4489b1daa0 | |||
96f0b5fbd6 | |||
f6897ad41f | |||
69e031da28 | |||
c61a7ef94a | |||
65bc22c02c | |||
1d55ea4dcf | |||
add30b33a1 | |||
999120e711 | |||
67ce0f7f2d | |||
4645ad06bc | |||
612437ae58 | |||
35a821b904 | |||
e4359834d6 | |||
ef7e39450c | |||
c3a5998d5c | |||
78ab2af8ba | |||
11354de596 | |||
86cac606e4 | |||
9b052c9aa5 | |||
8d709aea7d | |||
70ea8dd1a3 | |||
11c0d2a847 | |||
301b8a6ba3 | |||
acb0aa445c | |||
a90d578c85 | |||
d859228aa9 | |||
2be105d329 | |||
6d48fcfd6f | |||
551a315432 | |||
351dfdb892 | |||
dc6727fbf1 | |||
af88e48c39 | |||
9cfce68489 | |||
8393ff647c | |||
63122a5f51 | |||
0ad4598575 | |||
b71c28cec0 | |||
b0ab23dad4 | |||
de9297c351 | |||
26e3cb7957 | |||
bb78a3ca09 | |||
210680c9c9 | |||
6810eb31fd | |||
f5b6df483d | |||
7d0da7adc0 | |||
785f2b4ef5 | |||
b668a8c7d0 | |||
2e247faae4 | |||
3997a61b78 | |||
8ef27f0525 | |||
20855be968 | |||
800d13e3cd | |||
abc8399260 | |||
9ad2ba1131 | |||
0f8d6a98e3 | |||
81af8c74b4 | |||
16ab74294f | |||
f8bcc9d1ea | |||
9a89e3c3ca | |||
359c37f259 | |||
1ba5697986 | |||
647ed1e7aa | |||
b881e23c1c | |||
ae8621dc81 | |||
e08c3752f9 | |||
65646d1206 | |||
e42d3e8c4c | |||
a4642f6184 | |||
038c871911 | |||
f52dd2976f | |||
d079a57da4 | |||
43697a3476 | |||
b893bd1e39 | |||
c3b5a768e1 | |||
111ea44b40 | |||
d1b25c17b6 | |||
ba318f2939 | |||
7ac0291c53 | |||
f64676ab98 | |||
d522cbe1ca | |||
4fc7a4e436 | |||
79f2b4f0d5 | |||
880a7b1e25 | |||
89c5bb3080 | |||
a71fb8ca4d | |||
3b35aed3bf | |||
0bc3b9460f | |||
5509a3e9fd | |||
f84d0d0980 | |||
c866f6e46c | |||
3192f9d2ef | |||
de83a06db8 | |||
9260c8dce2 | |||
bea5f22732 | |||
363f12f81b | |||
1cbd33679f | |||
e962371b59 | |||
77a3d4d6f6 | |||
fc5fe6cf68 | |||
f921488e8c | |||
8fe08642f5 | |||
822632718f | |||
f66cd00646 | |||
5498e45a35 | |||
38479b3191 | |||
965fd8fc19 | |||
7a4f551a47 | |||
303fd74012 | |||
6a7d1b0c70 | |||
c1e6a28640 | |||
ec72f93480 | |||
8913fb515b | |||
6f91ff898f | |||
aa949103f6 | |||
14bd4ad74e | |||
f2507daa09 | |||
ff81c1e514 | |||
7ce92b47a0 | |||
bde5cc65da | |||
ec9347c3a4 | |||
ceb8dada1d | |||
f4186acf80 | |||
05ce54eac1 | |||
d28ecf3230 | |||
8562f723c5 | |||
f6d2043747 | |||
485818f3b5 | |||
ec28bd9c9e | |||
ad68dcf692 | |||
0b7e2a2c8c | |||
b6ebd0631a | |||
7ace4bdfa6 | |||
1cfbd4197d | |||
b2425d2c0e | |||
0ba914236a | |||
71ee0a6cf7 | |||
4326ad4d9c | |||
055bac6ff4 | |||
58713dc291 | |||
adf4aef517 | |||
a17bb2cc09 | |||
48d620f7cd | |||
fa7b104762 | |||
faf2fa167f | |||
ad1e68427c | |||
e9147f0f6f | |||
cddf630907 | |||
494a286cae | |||
e5e871ddcd | |||
5a3166e3de | |||
3149464c7a | |||
97d9b7816f | |||
ec77437080 | |||
d65882639a | |||
f8470287c1 | |||
3cc41ed62a | |||
445e37ccaf | |||
ed6427c541 | |||
90be01b05d | |||
1124293b9a | |||
0e804fdfd8 | |||
0443a35f2b | |||
3a148217e0 | |||
79d1892b66 | |||
0e06ac464f | |||
5ae83d8337 | |||
8694ee2c59 | |||
8234f7675a | |||
15cb0c4889 | |||
a3ebd9827f | |||
30d84f015a | |||
686414b03d | |||
6377618c12 | |||
f17e9c97b8 | |||
21fcdfaff6 | |||
4072edcced | |||
d704c10197 | |||
dea2a7f055 | |||
7e6eb4b9e4 | |||
61474fba5c | |||
42256384be | |||
e25f232fe5 | |||
f6d8f12ba2 | |||
d2b9e6fd8c | |||
cf7effacc0 | |||
1eb7aba293 | |||
21e916679c | |||
d574743c21 | |||
d8a2b82662 | |||
f4ebd890df | |||
b0be5de83a | |||
ba21ddd010 | |||
eecd7a0fd8 | |||
eb4c2f62a7 | |||
dc1e5e6512 | |||
adc0b183cd | |||
828b4f73d1 | |||
82a0761f49 | |||
904b9f07fb | |||
3c0acaa7da | |||
64c8420c9d | |||
26e3dc1aa7 | |||
3bc22a7b7c | |||
7e5b2695fe | |||
79afa79fd9 | |||
cdaaddb826 | |||
ec5f6a7cd8 | |||
72f34031a9 | |||
dc257b5cab | |||
4bdcd3d2ee | |||
b0650530cc | |||
454669ada2 | |||
a65975596e | |||
91f878cfc0 | |||
8c3e832cdc | |||
a5c670902d | |||
14be1d5f9f | |||
41d6d5c670 | |||
a090e6c21d | |||
d78c1ffa47 | |||
5808d20d88 | |||
7f4065e3da | |||
07daa51051 | |||
d10d4ce185 | |||
7f763e7881 | |||
5de0f66d7a | |||
354921ca92 | |||
fb28cc7d2f | |||
7cf89a9bf6 | |||
a3cbc549d8 | |||
1c48328347 | |||
9b69fe3c3c | |||
dc513a08f6 | |||
fcc44949a7 | |||
006764af66 | |||
b879d3f9ea | |||
7f4863da86 | |||
ba6f50d171 | |||
a803d4f646 | |||
85d940df66 | |||
6f9535ca34 | |||
019e2ac357 | |||
433916e18a | |||
f19588032f | |||
0595452c3d | |||
d6305df48e | |||
f7084580b2 | |||
3dd5f5858a | |||
02a06e1e7c | |||
50daf8ef73 | |||
fd5a34a1c4 | |||
51fda13684 | |||
a698b25fda | |||
89bd861d8e | |||
e5b7aae4ae | |||
031168ceed | |||
09a5788902 | |||
713664d103 | |||
f63391acf9 | |||
79ee4302ec | |||
9adda22921 | |||
25311f2a18 | |||
70c060b124 | |||
7a8a3c851b | |||
1096b2d212 | |||
ee286c5690 | |||
1da1d2e6fc | |||
64be9f936d | |||
30f24333c0 | |||
30f6a78282 | |||
8c9a0e0ff1 | |||
8268bbf700 | |||
e712e2f266 | |||
0807b6a2d9 | |||
83382cc8f7 | |||
8401aaeae2 | |||
abf5740950 | |||
606777508d | |||
e9ec6c67b2 | |||
69566f7fc3 | |||
6e4b299c7d | |||
ef35ebf79d | |||
1bc78edf71 | |||
d5204a09f7 | |||
4647aa70c0 | |||
2e8ec3ac64 | |||
25c6246e9f | |||
5408938007 | |||
50cb04b6f7 | |||
09fe4b11ad | |||
4157f21e06 | |||
4900230881 | |||
e60c0605e5 | |||
085781fa18 | |||
08ad8bd5d7 | |||
3d36e5f5d3 | |||
57319f26a6 | |||
11033683fd | |||
e5166c6c8e | |||
5c96663d1e | |||
ffb48c8669 | |||
12145a2393 | |||
f379866c1c | |||
dc030f4cd1 | |||
b726a2d778 | |||
a715ec9dc1 | |||
d24b871964 | |||
bd0c4e6034 | |||
95637e5608 | |||
93b394ed76 | |||
b515e427ff | |||
26b1acf5ef | |||
f31eb7c2b5 | |||
d423a6ea24 | |||
4211333e4e | |||
f220e380a7 | |||
9564b4e478 | |||
e2125b8ce9 | |||
0bb0e6ea4b | |||
cf512cc01b | |||
fe3e68c3af | |||
0bbfbe36c7 | |||
d6ae689593 | |||
5b5d1be52f | |||
cb808869dd | |||
64d83dccfb | |||
122253bb25 | |||
1d53db2854 | |||
30dc5ea1ea | |||
58c7ff1f1b | |||
57b9d634be | |||
6fb25dea88 | |||
10478e389a | |||
46fa4ee2a2 | |||
a7cefe44bf | |||
3d03585318 | |||
076c3428ee | |||
9d4ac46985 | |||
24edb867fa | |||
e926ac46c9 | |||
9ffd657dbb | |||
aa3cb39551 | |||
0acbdac66f | |||
5619bdbb67 | |||
381e63bfc9 | |||
e779347ff2 | |||
8fa906dd48 | |||
7e5ecd634d | |||
29cf4c1e89 | |||
73736abdea | |||
9ab411bade | |||
ef33156de7 | |||
6d8fd6e547 | |||
bab90a8bf2 | |||
43f0288c6c | |||
47e6371e2e | |||
5e400ed335 | |||
56a78b57af | |||
2bfeb7f42c | |||
37e0f12f89 | |||
fdd0e4a966 | |||
38df7e0038 | |||
e9efb78280 | |||
0424d7b640 | |||
2b80c7c91f | |||
142e2c0a65 | |||
1ed9ae7d60 | |||
329bf25dbd | |||
e18ffba183 | |||
ae3f0b429d | |||
34736c4e9b | |||
806678ee5f | |||
1e70401bdd | |||
3c7615f20d | |||
054d5e4879 | |||
b04ed43bed | |||
c2bbb952c5 | |||
63a9e1859b | |||
68ef069e6a | |||
72d4b21cb4 | |||
e8b931680d | |||
218b407f30 | |||
773cc27145 | |||
1c76e2e15b | |||
e466cfd6ff | |||
992b04d521 | |||
5c16c86871 | |||
78af9bbb10 | |||
b78c48d89f | |||
1f8c610bb0 | |||
d220728380 | |||
db58e9986c | |||
2a5e66eca6 | |||
e7e8ec296c | |||
80e69c56d0 | |||
ce3c6aecff | |||
300f76ba83 | |||
a4e8a38bf4 | |||
9bb04f43a8 | |||
9b0c08bd46 | |||
679d48e86e | |||
4b6bf60531 | |||
d8ce6648e2 | |||
402bc2d3a8 | |||
f3e193be0f | |||
f5b461612b | |||
d2514a3fc3 | |||
2019f9fdeb | |||
656f3e5cd9 | |||
741acfbba3 | |||
4b7eca02a0 | |||
c7788a80e1 | |||
08648894e3 | |||
4c5d5697bc | |||
c758a5b9ea | |||
2af54e2e28 | |||
cf9b23b987 | |||
f8380ec8e6 | |||
cd3245a631 | |||
144a155208 | |||
7f7ca13001 | |||
361e149063 | |||
fab85b381a | |||
faecf103bf | |||
a0f8c1aa8c | |||
24726b575d | |||
688b89a099 | |||
2e39110cc0 | |||
193cedae26 | |||
3d1f023ef5 | |||
9bb51d7146 | |||
84900aa588 | |||
b2bce3c4ac | |||
8c11620a42 | |||
7c1e0ac2e9 | |||
9a3c844336 | |||
69039d21c2 | |||
34c9cdd8bb | |||
0a8b30e824 | |||
ea5b521262 | |||
3bc71577b5 | |||
0fb24162fc | |||
d0d63f5bbb | |||
8041905144 | |||
37d96e238d | |||
ae8c941bfe | |||
4c16835ca4 | |||
7bd8922a4e | |||
42b2e2fcf0 | |||
f7256e9927 | |||
85444a5a6a | |||
ac115c7ea3 | |||
3c2a440553 | |||
b29ce3ee04 | |||
2b4394f3ce | |||
60f704eaa9 | |||
caa4fcf754 | |||
c4d1e4244e | |||
a6dc155028 | |||
9c65d11e66 | |||
26f50e7a74 | |||
e4773da1d7 | |||
dfd8086f63 | |||
ec513c61ad | |||
a96ab487ba | |||
cd28c985ce | |||
727af42ad4 | |||
e96d411f3e | |||
4a7d3d5945 | |||
03f05305cf | |||
af6e9ef85d | |||
8533a4a303 | |||
85f9234c74 | |||
064afd6705 | |||
978ff91f87 | |||
5cebbce7bb | |||
1cd0f02db5 | |||
77695bb505 | |||
4e4428fdbb | |||
6e978d74dd | |||
58974af77b | |||
11a0eae7a0 | |||
2a4ba895e5 | |||
19c019076e | |||
dfa18d3cb3 | |||
782c92885d | |||
a14dfa6cf1 | |||
f3b6f9d117 | |||
63b2b3feb6 | |||
9237dd25ac | |||
df9c4ce2fd | |||
fa5a7abbbf | |||
cd8bb7882e | |||
3c0378142a | |||
a524bffaa2 | |||
4b3decbe03 | |||
fa258a84cc | |||
5efa7309be | |||
6e6a01c33c | |||
94ce15bc2c | |||
b20ed34756 | |||
0b188b7ce9 | |||
80e0f20301 | |||
8410a709c9 | |||
13011cca23 | |||
53197856ab | |||
cb5bb69b47 | |||
4bfe52d73b | |||
58f557065e | |||
9d8228e1c6 | |||
fec840b897 | |||
8fbde4c452 | |||
0cc1765a1e | |||
f0346b1fd0 | |||
1bb798a8b8 | |||
4c35badcad | |||
6ba97cd961 | |||
af08a2f306 | |||
ded24432a5 | |||
bf124f196f | |||
6fc2d5eee1 | |||
dbe181ea84 | |||
dd169d356a | |||
4fbc016d18 |
@ -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
2
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
- **resin-cli version:**
|
||||
- **Operating system and architecture:**
|
19
.gitignore
vendored
19
.gitignore
vendored
@ -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,8 +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/
|
||||
resin-cli-*.zip
|
||||
|
5
.hound.yml
Normal file
5
.hound.yml
Normal file
@ -0,0 +1,5 @@
|
||||
coffee_script:
|
||||
config_file: coffeelint.json
|
||||
|
||||
javascript:
|
||||
enabled: false
|
28
.travis.yml
28
.travis.yml
@ -1,4 +1,28 @@
|
||||
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
|
||||
- provider: npm
|
||||
email: accounts@resin.io
|
||||
api_key:
|
||||
secure: phet6Du13hc1bzStbmpwy2ODNL5BFwjAmnpJ5wMcbWfI7fl0OtQ61s2+vW5hJAvm9fiRLOfiGAEiqOOtoupShZ1X8BNkC708d8+V+iZMoFh3+j6wAEz+N1sVq471PywlOuLAscOcqQNp92giCVt+4VPx2WQYh06nLsunvysGmUM=
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
condition: "$TRAVIS_TAG =~ ^v?[[:digit:]]+\\.[[:digit:]]+\\.[[:digit:]]+"
|
||||
repo: resin-io/resin-cli
|
||||
|
692
CHANGELOG.md
Normal file
692
CHANGELOG.md
Normal file
@ -0,0 +1,692 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file
|
||||
automatically by Versionist. DO NOT EDIT THIS FILE MANUALLY!
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## v6.10.2 - 2017-11-27
|
||||
|
||||
* Inline the entire resin-cli-auth module #721 [Tim Perry]
|
||||
|
||||
## v6.10.1 - 2017-11-27
|
||||
|
||||
* Set up TypeScript compilation, and make a small start on converting the CLI #720 [Tim Perry]
|
||||
* Don't commit raw JS build output #720 [Tim Perry]
|
||||
|
||||
## v6.10.0 - 2017-11-17
|
||||
|
||||
* Allow `os configure` to configure for an app, not just a specific device #718 [Tim Perry]
|
||||
* Print help even for expected errors #718 [Tim Perry]
|
||||
|
||||
## v6.9.0 - 2017-11-16
|
||||
|
||||
* Allow non-interactice config generate for simple network settings #714 [Tim Perry]
|
||||
* Fix issue where network settings were not used by `config generate` #714 [Tim Perry]
|
||||
|
||||
## v6.8.3 - 2017-11-16
|
||||
|
||||
* Remove resin promote command (which has never worked) to wait for larger resinOS provisioning updates #717 [Tim Perry]
|
||||
|
||||
## v6.8.2 - 2017-11-14
|
||||
|
||||
* Fix 'cannot read property R_OK of undefined' error in Node >=6 <6.3 #713 [Tim Perry]
|
||||
|
||||
## v6.8.1 - 2017-11-09
|
||||
|
||||
* Avoid AmbiguousApplication errors in device register when an id is used #711 [Tim Perry]
|
||||
|
||||
## v6.8.0 - 2017-10-27
|
||||
|
||||
* Allow preloading jetson-tx2 images, improve flasher images detection and remove the --dont-detect-flasher-type-images option. #706 [Alexis Svinartchouk]
|
||||
|
||||
## v6.7.4 - 2017-10-25
|
||||
|
||||
* Add preload to the CLI docs #702 [Tim Perry]
|
||||
|
||||
## v6.7.3 - 2017-10-25
|
||||
|
||||
* Allow specifying `--commit=latest` for `resin preload` #701 [Alexis Svinartchouk]
|
||||
|
||||
## v6.7.2 - 2017-10-24
|
||||
|
||||
* Make update-notifier more resilient and ensure it obeys NO_UPDATE_NOTIFIER, by updating it #699 [Tim Perry]
|
||||
|
||||
## v6.7.1 - 2017-10-24
|
||||
|
||||
* Respect the -dont-check-device-type option, fix error message #697 [Alexis Svinartchouk]
|
||||
|
||||
## v6.7.0 - 2017-10-18
|
||||
|
||||
* Added a device api key parameter to the `os configure` command. #487 [Pagan Gazzard]
|
||||
* Added a `--device-api-key` option to the `config generate` command. #487 [Pagan Gazzard]
|
||||
* Added a `--device-api-key` option to the `device register` command. #487 [Pagan Gazzard]
|
||||
|
||||
## v6.6.13 - 2017-10-18
|
||||
|
||||
* Fix issue where `os download` would always download prod images #689 [Tim Perry]
|
||||
|
||||
## v6.6.12 - 2017-10-16
|
||||
|
||||
* Update resin-preload to 4.0.2 to support preloading Edison images #687 [Alexis Svinartchouk]
|
||||
|
||||
## v6.6.11 - 2017-10-13
|
||||
|
||||
* Document how to `resin deploy` to an app as a collaborator #685 [Tim Perry]
|
||||
|
||||
## v6.6.10 - 2017-10-09
|
||||
|
||||
* Ensure hostname truly is optional when configuring device images #676 [Tim Perry]
|
||||
|
||||
## v6.6.9 - 2017-10-06
|
||||
|
||||
* Fix resin preload --splash-image argument handling #678 [Alexis Svinartchouk]
|
||||
|
||||
## v6.6.8 - 2017-10-06
|
||||
|
||||
* Ensure analytics failures (e.g. from broken tokens) at startup don't break commands #675 [Tim Perry]
|
||||
|
||||
## v6.6.7 - 2017-09-22
|
||||
|
||||
* Update to resin-sync, which fixes local push on windows #666 [Tim Perry]
|
||||
* Add windows instructions to fix node-gyp installs #666 [Tim Perry]
|
||||
|
||||
## v6.6.6 - 2017-09-11
|
||||
|
||||
* Create ISSUE_TEMPLATE.md #655 [Kostas Lekkas]
|
||||
|
||||
## v6.6.5 - 2017-08-31
|
||||
|
||||
* Fix lodash bugs in device move & quickstart #643 [Tim Perry]
|
||||
|
||||
## v6.6.4 - 2017-08-31
|
||||
|
||||
* Catch uncommitted build output automatically in Travis #644 [Tim Perry]
|
||||
|
||||
## v6.6.3 - 2017-08-31
|
||||
|
||||
* Update README to link to the full CLI command documentation #641 [Tim Perry]
|
||||
|
||||
## v6.6.2 - 2017-08-31
|
||||
|
||||
* Use DOCKER_HOST from env if possible, and no connection options are available #642 [Tim Perry]
|
||||
|
||||
## v6.6.1 - 2017-08-28
|
||||
|
||||
* Update resin-preload to 3.1.4 #650 [Alexis Svinartchouk]
|
||||
|
||||
## v6.6.0 - 2017-08-28
|
||||
|
||||
* Add a --dont-check-device-type option for `resin preload` #647 [Alexis Svinartchouk]
|
||||
|
||||
## v6.5.3 - 2017-08-24
|
||||
|
||||
* Remove resin-preload build filtering workaround. #645 [Alexis Svinartchouk]
|
||||
|
||||
## v6.5.2 - 2017-08-22
|
||||
|
||||
### Changed
|
||||
|
||||
- Progress bar when preload builds its docker image or fetches the build size;
|
||||
- Preload will do nothing if you try to preload a build that is already preloaded in the image.
|
||||
|
||||
## v6.5.1 - 2017-08-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix lodash upgrade bug that broke `resin help`
|
||||
|
||||
## v6.5.0 - 2017-08-18
|
||||
|
||||
### Changed
|
||||
|
||||
- Set up Travis npm autodeploy
|
||||
- Upgrade to Lodash v4, drastically reducing install size (due to dedupes)
|
||||
- Updated npm package description
|
||||
- Added support for looking up shared apps via [owner]/[appname] strings
|
||||
|
||||
### Added
|
||||
|
||||
- Use forked global-tunnel-ng that doesn't proxy connections to socket files
|
||||
- Preload support
|
||||
|
||||
## v6.4.0 - 2017-08-11
|
||||
|
||||
### Changed
|
||||
|
||||
- Support overlay2 in resin local push
|
||||
- Support Docker versions greater than 1.X in resin sync
|
||||
- Provide a helpful warning when Docker is not installed (or is unaccessible)
|
||||
- Added a link to the Node download page in the warning for users with old Node versions
|
||||
- Remove inconsistent (and now unneccesary) 'Tagging image as' message from local build output
|
||||
|
||||
## v6.3.1 - 2017-08-08
|
||||
|
||||
- Updated resin-cli-auth to point to the correct resin dashboard ued for authentication (for example, staging)
|
||||
|
||||
## v6.3.0 - 2017-08-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed compatibility of `resin local push` with docker >= 1.12
|
||||
|
||||
## v6.2.0 - 2017-07-27
|
||||
|
||||
### Changed
|
||||
|
||||
- Support the new resinOS versions where the sample connection file is called `resin-sample.ignore`
|
||||
|
||||
## v6.1.1 - 2017-07-18
|
||||
|
||||
### Changed
|
||||
|
||||
- Hide the intro quickstart message for now (until it gets renovated)
|
||||
|
||||
## v6.1.0 - 2017-06-30
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix issue where emulated builds broke Docker `ARG` commands
|
||||
- Fix issue when using resin deploy with non-standard stdin (e.g. git bash on windows)
|
||||
|
||||
### Added
|
||||
|
||||
- Bump resin-sync@8.0.1
|
||||
- Permit resin sync to collaborators
|
||||
- Fix "'cwd' must be a string" error in Node 8
|
||||
- Do not explicitly disable ControlMaster option for device SSH connections
|
||||
|
||||
## v6.0.0 - 2017-06-26
|
||||
|
||||
### Added
|
||||
|
||||
- Added support for `--squash` parameter for `resin build`
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Breaking** Remove Buffer polyfill, require Node v6+, and print warnings in older versions
|
||||
|
||||
## v5.11.1 - 2017-06-22
|
||||
|
||||
### Added
|
||||
|
||||
- Include Node version in Sentry logging
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ensure to send .pem file contents rather than filename to docker daemon
|
||||
- Add a polyfill to fix `local configure` in older (<6) Node versions
|
||||
|
||||
## v5.11.0 - 2017-06-19
|
||||
|
||||
### Added
|
||||
|
||||
- `package-lock.json` for `npm5` users
|
||||
- Added ability to run an emulated build silently with `resin build`
|
||||
- Gzip images when uploading in `resin deploy`
|
||||
- Show a clear message immediately as the deploy starts, if we're deploying an image.
|
||||
- Forced update to the newest resin-sdk (^6.4.1)
|
||||
- Allow OS version selection when doing `resin device init`
|
||||
- Actually tolerate the `--yes` param to `resin device init`
|
||||
- Allows passing `--drive` to `resin device init`
|
||||
- List detected drives with `resin util available-drives`
|
||||
- Add the `resin os build-config` method to pass the interactive config step once
|
||||
and reuse the built file for subsequent `resin os configure` calls (added the new `--config` param to it),
|
||||
and for `resin device init` (same `--config` param)
|
||||
- Improve the supported device types listing
|
||||
|
||||
### Fixed
|
||||
|
||||
- Ensure emulated builds use the correct relative path to qemu when called from any location
|
||||
- Make emulated builds reliable in the presence for WORKDIR comands
|
||||
|
||||
## v5.10.2 - 2017-05-31
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed command line arguments for `resin build`
|
||||
|
||||
## v5.10.1 - 2017-05-22
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed breaking bug in `resin local ssh`
|
||||
|
||||
## v5.10.0 - 2017-05-22
|
||||
|
||||
### Added
|
||||
|
||||
- Reduce granularity of update checking to one day
|
||||
- Include extra usage metadata in error logging to help debugging
|
||||
- Add uploading of build logs when present with resin deploy
|
||||
- Highlight cache usage in a local build
|
||||
- Show a progress bar for upload progress
|
||||
- Add ability to specify build-time variables for local builds
|
||||
- Added the proxy support for the `resin ssh` command
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed the not enough unicorns bug in resin build
|
||||
- Removed the install-time warning for the `valid-email` package
|
||||
|
||||
## v5.9.1 - 2017-05-01
|
||||
|
||||
### Fixed
|
||||
|
||||
- Technical release because v5.9.0 was published earlier (erroneously)
|
||||
|
||||
## v5.9.0 - 2017-05-01
|
||||
|
||||
### Added
|
||||
|
||||
- HTTP(S) proxy support
|
||||
|
||||
## v5.8.1 - 2017-04-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- The `ssh` command was broken
|
||||
|
||||
## v5.8.0 - 2017-04-26
|
||||
|
||||
### Added
|
||||
|
||||
- Add cloud builder output to local build
|
||||
- Add nocache and tag options to resin deploy
|
||||
- Add ability to build and deploy an image to resin's infrastructure
|
||||
|
||||
### Fixed
|
||||
|
||||
- Capture and report errors happening during the program initialization, like parsing invalid YAML config
|
||||
|
||||
## v5.7.2 - 2017-04-18
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed warning on NPM install due to dependency conflicts
|
||||
- Improved node v4 support (some operations are fixed, some will run faster)
|
||||
|
||||
## v5.7.1 - 2017-04-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Add basic support for the new ResinOS version format
|
||||
|
||||
## v5.7.0 - 2017-03-28
|
||||
|
||||
### Fixed
|
||||
|
||||
- The OS init issues:
|
||||
* failing to get the superuser resin auth (tried to look into `/root/.resin`)
|
||||
* failing to write to the drive
|
||||
|
||||
## v5.6.1 - 2017-03-23
|
||||
|
||||
### Added
|
||||
|
||||
- Add Sentry error tracking
|
||||
|
||||
### Fixed
|
||||
|
||||
- The unneeded warning about the default OS version download from the `device init` command.
|
||||
- Changed the help references from gitter to forums.
|
||||
|
||||
## v5.6.0 - 2017-03-23
|
||||
|
||||
### Added
|
||||
|
||||
- The `--version` option to the `os download` command. Check `resin help os download` for details.
|
||||
|
||||
## v5.5.0 - 2017-03-10
|
||||
|
||||
### Added
|
||||
|
||||
- Require superuser for scan commands, also introduce docker timeout
|
||||
- Bump resin-sync@7.0.0: use experimental rds which requires superuser permissions
|
||||
|
||||
## v5.4.0 - 2017-03-09
|
||||
|
||||
### Added
|
||||
|
||||
- Implement 'resin local stop'
|
||||
- Implement 'resin local'
|
||||
- Implement 'resin local push'
|
||||
- Implement 'resin local ssh'
|
||||
- Implement 'resin local scan'
|
||||
- Implement 'resin local promote'
|
||||
- Implement 'resin local logs'
|
||||
- Implement 'resin local flash'
|
||||
- Implement 'resin local configure'
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove app create from primary commands
|
||||
|
||||
## v5.3.0 - 2017-03-03
|
||||
|
||||
### Added
|
||||
|
||||
- `resin sync` AUFS device support
|
||||
|
||||
### Changed
|
||||
|
||||
- Moved to the new version of `resin-sdk` (via `resin-sdk-preconfigured`)
|
||||
|
||||
## v5.2.4 - 2017-01-18
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix documented requirements for resin ssh and resin sync
|
||||
|
||||
## v5.2.3 - 2017-01-04
|
||||
|
||||
### Changed
|
||||
|
||||
- Add missing `js-yaml` dependency.
|
||||
|
||||
## v5.2.2 - 2016-11-01
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix `shutdown` command not being available.
|
||||
|
||||
## v5.2.1 - 2016-10-28
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix `Boolean options can't have parameters` error in every command.
|
||||
|
||||
## v5.2.0 - 2016-10-27
|
||||
|
||||
### Added
|
||||
|
||||
- Add `shutdown` command.
|
||||
- Add `--force` option to `device reboot` command.
|
||||
|
||||
## v5.1.0 - 2016-09-25
|
||||
|
||||
### Added
|
||||
|
||||
- Add `devices supported` command.
|
||||
|
||||
## v5.0.0 - 2016-09-15
|
||||
|
||||
### Added
|
||||
|
||||
- Automatically parse '.gitignore' for file inclusions/exclusions from resin sync by default. Skip parsing with `--skip-gitignore`.
|
||||
- Automatically save options to `<sourceDirectory>/.resin-sync.yml` after every run.
|
||||
- Support user-specified destination directories with `--destination/-d` option.
|
||||
- Implement `--after` option to perform actions local (e.g. cleanup) after resin sync has finished.
|
||||
- Implement interactive dialog for destination directory, with `/usr/src/app` being the default choice.
|
||||
|
||||
### Changed
|
||||
|
||||
- Require `resin sync` `--source/-s` option if a `.resin-sync.yml` file is not found in the current directory.
|
||||
- Require `uuid` as an argument in `resin sync/ssh` (`appName` has been removed).
|
||||
- Always display interactive device selection dialog when uuid is not passed as an argument.
|
||||
- Disable ControlMaster ssh option (as reported in support).
|
||||
|
||||
## v4.5.0 - 2016-09-14
|
||||
|
||||
### Added
|
||||
|
||||
- Attempt to retrieve device type from the image's first partition.
|
||||
|
||||
## v4.4.0 - 2016-08-11
|
||||
|
||||
### Changed
|
||||
|
||||
- Display OS and Supervisor version in `devices` and `device` commands.
|
||||
|
||||
## v4.3.0 - 2016-08-11
|
||||
|
||||
### Added
|
||||
|
||||
- Implement `device public-url enable` command.
|
||||
- Implement `device public-url disable` command.
|
||||
- Implement `device public-url status` command.
|
||||
- Implement `device public-url` command.
|
||||
- Add global `--help` option.
|
||||
|
||||
## v4.2.1 - 2016-07-26
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix log messages being `undefined`.
|
||||
|
||||
## v4.2.0 - 2016-06-22
|
||||
|
||||
### Added
|
||||
|
||||
- Add `verbose` option to `resin sync`.
|
||||
|
||||
## v4.1.0 - 2016-06-22
|
||||
|
||||
### Added
|
||||
|
||||
- Add `verbose` option to `resin ssh`.
|
||||
|
||||
## v4.0.3 - 2016-05-17
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix `resin ssh` errors when running in `cmd.exe`.
|
||||
|
||||
## v4.0.2 - 2016-04-27
|
||||
|
||||
### Changed
|
||||
|
||||
- Upgrade `resin-sync` to v2.0.2.
|
||||
|
||||
## v4.0.1 - 2016-04-26
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix unhandled exceptions in the `resin ssh` command.
|
||||
|
||||
## v4.0.0 - 2016-04-26
|
||||
|
||||
### Added
|
||||
|
||||
- Implement `resin ssh` command
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove `resin sync` `exec` option.
|
||||
- Upgrade `resin-sync` to v2.0.1.
|
||||
- Upgrade `resin-sdk` to v5.3.0.
|
||||
|
||||
## v3.0.2 - 2016-04-08
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix `os configure` command not working with shorter uuids.
|
||||
|
||||
## v3.0.1 - 2016-03-29
|
||||
|
||||
### Changed
|
||||
|
||||
- Log Mixpanel events based on the matching command signature.
|
||||
|
||||
## v3.0.0 - 2016-03-28
|
||||
|
||||
### Added
|
||||
|
||||
- Implement `config inject` command.
|
||||
- Document the case where `EACCES` is thrown during login because of an expired token.
|
||||
- Integrate `resin-plugin-sync` as a build-in command.
|
||||
|
||||
### Changed
|
||||
|
||||
- Allow `config generate` to generate a `config.json` for an application.
|
||||
- Force update alert to always be shown.
|
||||
- Only throw "Invalid 2FA code" if we're sure that's the cause during `login`.
|
||||
|
||||
## v2.7.0 - 2016-03-07
|
||||
|
||||
### Added
|
||||
|
||||
- Implement `config generate` command.
|
||||
- Implement `device reboot` command.
|
||||
|
||||
## v2.6.2 - 2016-02-19
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove debugging statement in `quickstart`.
|
||||
|
||||
## v2.6.1 - 2016-02-12
|
||||
|
||||
### Added
|
||||
|
||||
- Documented corrupted image MBR error.
|
||||
- Show parser device status in `device` command.
|
||||
- Show id a device is online in `devices` command.
|
||||
- Make use of static images.
|
||||
|
||||
### Changed
|
||||
|
||||
- Improve analytics.
|
||||
- Improve `quickstart` messages.
|
||||
- Fix `device` example.
|
||||
|
||||
## v2.6.0 - 2016-01-21
|
||||
|
||||
### Added
|
||||
|
||||
- Add support for credential-based authentication.
|
||||
- Redirect users to GitHub and Gitter in case of errors.
|
||||
- Add Resin.io ASCII art header on `login`.
|
||||
- Print an informative next-steps message after `login`.
|
||||
- Print informative verbose help to `resin help`.
|
||||
- Support for shorter uuids in all commands.
|
||||
|
||||
### Changed
|
||||
|
||||
- Change license to Apache 2.0.
|
||||
- Don't make `device init` a primary command.
|
||||
- Stop instructing users to run `quickstart` as root.
|
||||
- Make `login` command purely interactive.
|
||||
- Handle authentication in `quickstart` if user is not logged in.
|
||||
- Redirect to `signup` from `login` if user doesn't have an account.
|
||||
- Make sure to remove registered device resource in case of errors in `quickstart`.
|
||||
- Upgrade Resin SDK to v5.0.1.
|
||||
- Upgrade Resin Image Manager to v3.2.6.
|
||||
- Make `devices` output shorter uuids.
|
||||
|
||||
## v2.5.0 - 2015-12-11
|
||||
|
||||
### Added
|
||||
|
||||
- Show device id in `resin devices`.
|
||||
- Add helpful instructions after `resin quickstart`.
|
||||
- Add timestamp to `resin logs` lines.
|
||||
|
||||
### Changed
|
||||
|
||||
- Lazy load command actions dependencies for performance reasons.
|
||||
|
||||
## v2.4.0 - 2015-12-01
|
||||
|
||||
### Added
|
||||
|
||||
- Show device types when selecting applications.
|
||||
- Automatic token exchange login with the web dashboard.
|
||||
|
||||
### Changed
|
||||
|
||||
- Simplify download output messages.
|
||||
|
||||
## v2.3.0 - 2015-11-20
|
||||
|
||||
### Added
|
||||
|
||||
- Implement `settings` command.
|
||||
- Handle Windows elevation automatically.
|
||||
|
||||
### Changed
|
||||
|
||||
- Show uuids in `devices` command.
|
||||
- Clarify resin url in `login` and `whoami`.
|
||||
|
||||
## v2.2.0 - 2015-11-12
|
||||
|
||||
### Added
|
||||
|
||||
- Implement `device move` command.
|
||||
|
||||
## v2.1.0 - 2015-11-11
|
||||
|
||||
### Added
|
||||
|
||||
- Implement `config read` command.
|
||||
- Implement `config write` command.
|
||||
|
||||
### Changed
|
||||
|
||||
- Clarify the need of computer password during `sudo` in `os initialize`.
|
||||
|
||||
## v2.0.1 - 2015-10-26
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix critical error when elevating permissions.
|
||||
|
||||
## v2.0.0 - 2015-10-26
|
||||
|
||||
### Added
|
||||
|
||||
- Add `drive` option to `os initialize`.
|
||||
- Add application name length validation.
|
||||
- Allow passing a custom uuid to `device register`.
|
||||
- Add `advanced` option in `device init`.
|
||||
- Implement user/password login form with 2FA support.
|
||||
|
||||
### Changed
|
||||
|
||||
- Clarify the need for admin privileges on update.
|
||||
- Fix non working `yes` option in `os initialize`.
|
||||
- Fix non working `type` option in `app create`.
|
||||
- Take device type as an option in `os initialize`.
|
||||
- Improve the way the update notifier is shown.
|
||||
- Ignore advanced configuration options by default.
|
||||
- Fix `device info` shadowing other command help pages.
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove login with token functionality.
|
||||
- Remove project directory creation logic in `device init`.
|
||||
- Remove `app associate` command.
|
||||
|
||||
## v1.1.0 - 2015-10-13
|
||||
|
||||
### Added
|
||||
|
||||
- Implement `os download` command.
|
||||
- Implement `os configure` command.
|
||||
- Implement `os initialize` command.
|
||||
- Implement `device register` command.
|
||||
- Add TROUBLESHOOTING guide.
|
||||
- Add a dynamic widget for selecting connected drives.
|
||||
|
||||
### Changed
|
||||
|
||||
- Make sure temporary files are removed on failures in `device init`.
|
||||
- Prompt to select application if running `device init` with no arguments.
|
||||
- Only use admin privileges in `os initialize` to avoid the cache directory to belong to `root`.
|
||||
- Separate help output per relevance.
|
||||
- Only print primary command help by default.
|
||||
- Print plugin warnings in red as the rest of the errors.
|
||||
- Shorten the length of device await message.
|
||||
- Upgrade Resin SDK to v3.0.0.
|
||||
- Check that a directory is a `git` repository before attempting to run `git` operations on it.
|
||||
- Improve plugin scanning mechanism.
|
||||
- Fix `EPERM` issue in Windows after a successfull `device init`.
|
||||
- Fix SDCard burning issues in Windows 10.
|
||||
- Fix plugins not being loaded in Windows 10.
|
||||
- Fix bug when listing the available drives in Windows when the user name contains spaces.
|
||||
- Fix an edge case that caused drives to be scanned incorrectly in OS X.
|
||||
- Fix operating system not being detected correctly when initializing a device.
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove outdated information from README.
|
177
LICENSE
Normal file
177
LICENSE
Normal file
@ -0,0 +1,177 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"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
|
131
README.md
131
README.md
@ -1,65 +1,124 @@
|
||||
# Resin CLI
|
||||
Resin CLI
|
||||
=========
|
||||
|
||||
> The official resin.io CLI tool.
|
||||
|
||||
[](http://badge.fury.io/js/resin-cli)
|
||||
[](https://david-dm.org/resin-io/resin-cli.png)
|
||||
[](https://travis-ci.org/resin-io/resin-cli)
|
||||
[](https://david-dm.org/resin-io/resin-cli)
|
||||
[](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) (>= v4)
|
||||
- [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 --global --production resin-cli
|
||||
```
|
||||
|
||||
### Running locally
|
||||
### 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 in 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!
|
||||
|
||||
### 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
|
||||
---
|
||||
|
||||
The following command will watch for any changes and will run a linter and the whole test suite:
|
||||
Plugins
|
||||
-------
|
||||
|
||||
```sh
|
||||
$ gulp watch
|
||||
```
|
||||
The Resin CLI can be extended with plugins to automate laborious tasks and overall provide a better experience when working with Resin.io. Check the [plugin development tutorial](https://github.com/resin-io/resin-plugin-hello) to learn how to build your own!
|
||||
|
||||
If you set `DEBUG` environment variable, errors will print with a stack trace:
|
||||
FAQ
|
||||
---
|
||||
|
||||
```sh
|
||||
$ DEBUG=true resin ...
|
||||
```
|
||||
### Where is my configuration file?
|
||||
|
||||
## Documentation
|
||||
The per-user configuration file lives in `$HOME/.resinrc.yml` or `%UserProfile%\_resinrc.yml`, in Unix based operating systems and Windows respectively.
|
||||
|
||||
You can renegerate the documentation with:
|
||||
The Resin CLI also attempts to read a `resinrc.yml` file in the current directory, which takes precedence over the per-user configuration file.
|
||||
|
||||
```sh
|
||||
$ npm run-script doc
|
||||
```
|
||||
### How do I point the Resin CLI to staging?
|
||||
|
||||
## Manual pages
|
||||
The easiest way is to set the `RESINRC_RESIN_URL=resinstaging.io` environment variable.
|
||||
|
||||
UNIX manual pages reside in `man/`
|
||||
Alternatively, you can edit your configuration file and set `resinUrl: resinstaging.io` to persist this setting.
|
||||
|
||||
You can regenerate UNIX `roff` manual pages from markdown with:
|
||||
### How do I make the Resin CLI persist data in another directory?
|
||||
|
||||
```sh
|
||||
$ gulp man
|
||||
```
|
||||
The Resin CLI persists your session token, as well as cached images in `$HOME/.resin` or `%UserProfile%\_resin`.
|
||||
|
||||
If you add a new `man` page, remember to add the generated filename to the `man` array in `package.json`.
|
||||
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.
|
||||
|
||||
## Caveats
|
||||
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.
|
||||
|
||||
- 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).
|
||||
Support
|
||||
-------
|
||||
|
||||
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
59
TROUBLESHOOTING.md
Normal 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
34
appveyor.yml
Normal 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')
|
34
automation/build-bin.ts
Executable file
34
automation/build-bin.ts
Executable file
@ -0,0 +1,34 @@
|
||||
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')
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
35
automation/custom-types.d.ts
vendored
Normal file
35
automation/custom-types.d.ts
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
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;
|
||||
}
|
53
automation/deploy-bin.ts
Normal file
53
automation/deploy-bin.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import * as Promise from 'bluebird';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs-extra';
|
||||
import * as publishRelease from 'publish-release';
|
||||
import * as archiver from 'archiver';
|
||||
|
||||
const publishReleaseAsync = Promise.promisify(publishRelease);
|
||||
|
||||
const { GITHUB_TOKEN } = process.env;
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
|
||||
const version = 'v' + require('../package.json').version;
|
||||
const outputFile = path.join(ROOT, `resin-cli-${version}-${os.platform()}-${os.arch()}.zip`);
|
||||
|
||||
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: 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);
|
||||
});
|
15
automation/tsconfig.json
Normal file
15
automation/tsconfig.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"preserveConstEnums": true,
|
||||
"removeComments": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": [
|
||||
"./**/*.ts"
|
||||
]
|
||||
}
|
@ -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);
|
||||
});
|
@ -1,128 +0,0 @@
|
||||
(function() {
|
||||
var _, async, commandOptions, resin, visuals;
|
||||
|
||||
_ = require('lodash-contrib');
|
||||
|
||||
async = require('async');
|
||||
|
||||
resin = require('resin-sdk');
|
||||
|
||||
visuals = require('resin-cli-visuals');
|
||||
|
||||
commandOptions = require('./command-options');
|
||||
|
||||
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 <id> 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 <id>',
|
||||
description: 'list a single application',
|
||||
help: 'Use this command to show detailed information for a single application.\n\nExamples:\n\n $ resin app 91',
|
||||
permission: 'user',
|
||||
action: function(params, options, done) {
|
||||
return resin.models.application.get(params.id, 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 <id>',
|
||||
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 91',
|
||||
permission: 'user',
|
||||
action: function(params, options, done) {
|
||||
return resin.models.application.restart(params.id, done);
|
||||
}
|
||||
};
|
||||
|
||||
exports.remove = {
|
||||
signature: 'app rm <id>',
|
||||
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 91\n $ resin app rm 91 --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.id, callback);
|
||||
}, done);
|
||||
}
|
||||
};
|
||||
|
||||
exports.init = {
|
||||
signature: 'init <id>',
|
||||
description: 'init an application',
|
||||
help: 'Use this command to associate a local project to an existing resin.io application.\n\nThe application should be a git repository before issuing this command.\nNotice this command adds a `resin` git remote to your application.\n\nExamples:\n\n $ cd myApp && resin init 91',
|
||||
permission: 'user',
|
||||
action: function(params, options, done) {
|
||||
var currentDirectory;
|
||||
currentDirectory = process.cwd();
|
||||
return async.waterfall([
|
||||
function(callback) {
|
||||
return resin.vcs.isResinProject(currentDirectory, callback);
|
||||
}, function(isResinProject, callback) {
|
||||
var error;
|
||||
if (isResinProject) {
|
||||
error = new Error('Project is already a resin application.');
|
||||
return callback(error);
|
||||
}
|
||||
return callback();
|
||||
}, function(callback) {
|
||||
return resin.models.application.get(params.id, callback);
|
||||
}, function(application, callback) {
|
||||
return resin.vcs.initProjectWithApplication(application, currentDirectory, callback);
|
||||
}
|
||||
], done);
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
@ -1,134 +0,0 @@
|
||||
(function() {
|
||||
var _, async, resin, url, visuals;
|
||||
|
||||
_ = require('lodash-contrib');
|
||||
|
||||
url = require('url');
|
||||
|
||||
async = require('async');
|
||||
|
||||
resin = require('resin-sdk');
|
||||
|
||||
visuals = require('resin-cli-visuals');
|
||||
|
||||
exports.login = {
|
||||
signature: 'login',
|
||||
description: 'login to resin.io',
|
||||
help: 'Use this command to login to your resin.io account.\nYou need to login before you can use most of the commands this tool provides.\n\nYou can pass your credentials as `--username` and `--password` options, or you can omit the\ncredentials, in which case the tool will present you with an interactive login form.\n\nExamples:\n\n $ resin login --username <username> --password <password>\n $ resin login',
|
||||
options: [
|
||||
{
|
||||
signature: 'username',
|
||||
parameter: 'username',
|
||||
description: 'user name',
|
||||
alias: 'u'
|
||||
}, {
|
||||
signature: 'password',
|
||||
parameter: 'password',
|
||||
description: 'user password',
|
||||
alias: 'p'
|
||||
}
|
||||
],
|
||||
action: function(params, options, done) {
|
||||
var hasOptionCredentials;
|
||||
hasOptionCredentials = !_.isEmpty(options);
|
||||
if (hasOptionCredentials) {
|
||||
if (!options.username) {
|
||||
return done(new Error('Missing username'));
|
||||
}
|
||||
if (!options.password) {
|
||||
return done(new Error('Missing password'));
|
||||
}
|
||||
}
|
||||
return async.waterfall([
|
||||
function(callback) {
|
||||
if (hasOptionCredentials) {
|
||||
return callback(null, options);
|
||||
} else {
|
||||
return visuals.widgets.login(callback);
|
||||
}
|
||||
}, function(credentials, callback) {
|
||||
return resin.auth.login(credentials, callback);
|
||||
}
|
||||
], 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);
|
@ -1,38 +0,0 @@
|
||||
(function() {
|
||||
exports.yes = {
|
||||
signature: 'yes',
|
||||
description: 'confirm non interactively',
|
||||
boolean: true,
|
||||
alias: 'y'
|
||||
};
|
||||
|
||||
exports.application = {
|
||||
signature: 'application',
|
||||
parameter: 'application',
|
||||
description: 'application id',
|
||||
alias: ['a', 'app'],
|
||||
required: 'You have to specify an application'
|
||||
};
|
||||
|
||||
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);
|
@ -1,140 +0,0 @@
|
||||
(function() {
|
||||
var _, async, commandOptions, osAction, path, resin, visuals;
|
||||
|
||||
_ = require('lodash-contrib');
|
||||
|
||||
path = require('path');
|
||||
|
||||
async = require('async');
|
||||
|
||||
resin = require('resin-sdk');
|
||||
|
||||
visuals = require('resin-cli-visuals');
|
||||
|
||||
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 91',
|
||||
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 <id>',
|
||||
description: 'list a single device',
|
||||
help: 'Use this command to show information about a single device.\n\nExamples:\n\n $ resin device 317',
|
||||
permission: 'user',
|
||||
action: function(params, options, done) {
|
||||
return resin.models.device.get(params.id, 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 <id>',
|
||||
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 317\n $ resin device rm 317 --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.id, 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 <id> [name]',
|
||||
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 317 MyPi\n $ resin device rename 317',
|
||||
permission: 'user',
|
||||
action: function(params, options, done) {
|
||||
return async.waterfall([
|
||||
function(callback) {
|
||||
if (!_.isEmpty(params.name)) {
|
||||
return callback(null, params.name);
|
||||
}
|
||||
return visuals.widgets.ask('How do you want to name this device?', callback);
|
||||
}, function(name, callback) {
|
||||
return resin.models.device.rename(params.id, name, 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\nExamples:\n\n $ resin device init --application 91 --network ethernet\n $ resin device init /dev/disk2 --application 91 --network wifi --ssid MyNetwork --key secret',
|
||||
options: [commandOptions.application, commandOptions.network, commandOptions.wifiSsid, commandOptions.wifiKey],
|
||||
permission: 'user',
|
||||
action: function(params, options, done) {
|
||||
params.id = options.application;
|
||||
return async.waterfall([
|
||||
function(callback) {
|
||||
if (params.device != null) {
|
||||
return callback(null, params.device);
|
||||
}
|
||||
return visuals.patterns.selectDrive(callback);
|
||||
}, function(device, callback) {
|
||||
params.device = device;
|
||||
return visuals.patterns.confirm(options.yes, "This will completely erase " + params.device + ". Are you sure you want to continue?", 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);
|
@ -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);
|
@ -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);
|
@ -1,92 +0,0 @@
|
||||
(function() {
|
||||
var _, async, examplesData, fs, gitCli, path, resin, visuals;
|
||||
|
||||
async = require('async');
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
path = require('path');
|
||||
|
||||
_ = require('lodash');
|
||||
|
||||
gitCli = require('git-cli');
|
||||
|
||||
resin = require('resin-sdk');
|
||||
|
||||
visuals = require('resin-cli-visuals');
|
||||
|
||||
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 example;
|
||||
example = examplesData[params.id - 1];
|
||||
if (example == null) {
|
||||
return done(new Error("Unknown example: " + id));
|
||||
}
|
||||
return async.waterfall([
|
||||
function(callback) {
|
||||
var exampleAbsolutePath;
|
||||
exampleAbsolutePath = path.join(process.cwd(), example.name);
|
||||
return fs.exists(exampleAbsolutePath, function(exists) {
|
||||
var error;
|
||||
if (!exists) {
|
||||
return callback();
|
||||
}
|
||||
error = new Error("Directory exists: " + example.name);
|
||||
return callback(error);
|
||||
});
|
||||
}, function(callback) {
|
||||
console.info("Cloning " + example.display_name + " to " + example.name);
|
||||
return gitCli.Repository.clone(example.repository, example.name, callback);
|
||||
}
|
||||
], done);
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
@ -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);
|
@ -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);
|
@ -1,16 +0,0 @@
|
||||
(function() {
|
||||
var packageJSON;
|
||||
|
||||
packageJSON = require('../../package.json');
|
||||
|
||||
exports.version = {
|
||||
signature: 'version',
|
||||
description: 'output the version number',
|
||||
help: 'Display the Resin CLI, as well as the bundled NodeJS version.',
|
||||
action: function() {
|
||||
console.log(packageJSON.name + ": " + packageJSON.version);
|
||||
return console.log("node: " + process.version);
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
@ -1,92 +0,0 @@
|
||||
(function() {
|
||||
var _, 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();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
var sshKeyWidth;
|
||||
if (error != null) {
|
||||
return done(error);
|
||||
}
|
||||
sshKeyWidth = resin.settings.get('sshKeyWidth');
|
||||
key.public_key = '\n' + visuals.helpers.chop(key.public_key, sshKeyWidth);
|
||||
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);
|
@ -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);
|
@ -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 <id>.\n\nExamples:\n\n $ resin note "My useful note" --device 317\n $ cat note.txt | resin note --device 317',
|
||||
options: [
|
||||
{
|
||||
signature: 'device',
|
||||
parameter: 'device',
|
||||
description: 'device id',
|
||||
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);
|
@ -1,137 +0,0 @@
|
||||
(function() {
|
||||
var _, async, commandOptions, diskio, fs, mkdirp, os, path, progressStream, resin, visuals;
|
||||
|
||||
_ = require('lodash-contrib');
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
os = require('os');
|
||||
|
||||
async = require('async');
|
||||
|
||||
path = require('path');
|
||||
|
||||
mkdirp = require('mkdirp');
|
||||
|
||||
resin = require('resin-sdk');
|
||||
|
||||
visuals = require('resin-cli-visuals');
|
||||
|
||||
progressStream = require('progress-stream');
|
||||
|
||||
diskio = require('diskio');
|
||||
|
||||
commandOptions = require('./command-options');
|
||||
|
||||
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\nBy default, this command saved the downloaded image into a resin specific directory.\nYou can save it to a custom location by specifying the `--output` option.\n\nExamples:\n\n $ resin os download 91 --network ethernet\n $ resin os download 91 --network wifi --ssid MyNetwork --key secreykey123\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'
|
||||
}
|
||||
],
|
||||
permission: 'user',
|
||||
action: function(params, options, done) {
|
||||
var fileName, osParams;
|
||||
osParams = {
|
||||
network: options.network,
|
||||
wifiSsid: options.ssid,
|
||||
wifiKey: options.key,
|
||||
appId: params.id
|
||||
};
|
||||
fileName = resin.models.os.generateCacheName(osParams);
|
||||
if (options.output == null) {
|
||||
options.output = path.join(resin.settings.get('directories.os'), fileName);
|
||||
}
|
||||
return async.waterfall([
|
||||
function(callback) {
|
||||
return mkdirp(path.dirname(options.output), _.unary(callback));
|
||||
}, function(callback) {
|
||||
var bar;
|
||||
console.info("Destination file: " + options.output + "\n");
|
||||
bar = new visuals.widgets.Progress('Downloading Device OS');
|
||||
return resin.models.os.download(osParams, options.output, callback, function(state) {
|
||||
return bar.update(state);
|
||||
});
|
||||
}
|
||||
], 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) {
|
||||
return async.waterfall([
|
||||
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, error, imageFileSize, imageFileStream, progress;
|
||||
if (!confirmed) {
|
||||
return done();
|
||||
}
|
||||
imageFileSize = fs.statSync(params.image).size;
|
||||
if (imageFileSize === 0) {
|
||||
error = new Error("Invalid OS image: " + params.image + ". The image is 0 bytes.");
|
||||
return callback(error);
|
||||
}
|
||||
progress = progressStream({
|
||||
length: imageFileSize,
|
||||
time: 500
|
||||
});
|
||||
if (!options.quiet) {
|
||||
bar = new visuals.widgets.Progress('Writing Device OS');
|
||||
progress.on('progress', function(status) {
|
||||
return bar.update(status);
|
||||
});
|
||||
}
|
||||
imageFileStream = fs.createReadStream(params.image).pipe(progress);
|
||||
return diskio.writeStream(params.device, imageFileStream, callback);
|
||||
}
|
||||
], function(error) {
|
||||
var resinWritePath, windosu;
|
||||
if (error == null) {
|
||||
return done();
|
||||
}
|
||||
if (_.all([os.platform() === 'win32', error.code === 'EPERM' || error.code === 'EACCES', !options.fromScript])) {
|
||||
windosu = require('windosu');
|
||||
resinWritePath = "\"" + (path.join(__dirname, '..', '..', 'bin', 'resin-write')) + "\"";
|
||||
return windosu.exec("\"" + process.argv[0] + "\" " + resinWritePath + " \"" + params.image + "\" \"" + params.device + "\"");
|
||||
} else {
|
||||
return done(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
@ -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);
|
@ -1,23 +0,0 @@
|
||||
(function() {
|
||||
var open, resin, url;
|
||||
|
||||
open = require('open');
|
||||
|
||||
url = require('url');
|
||||
|
||||
resin = require('resin-sdk');
|
||||
|
||||
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, preferencesUrl;
|
||||
preferencesUrl = resin.settings.get('urls.preferences');
|
||||
absUrl = url.resolve(resin.settings.get('remoteUrl'), preferencesUrl);
|
||||
return open(absUrl);
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
@ -1,41 +0,0 @@
|
||||
(function() {
|
||||
var _, async, npm, packageJSON;
|
||||
|
||||
async = require('async');
|
||||
|
||||
_ = require('lodash-contrib');
|
||||
|
||||
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 async.waterfall([
|
||||
function(callback) {
|
||||
options = {
|
||||
loglevel: 'silent',
|
||||
global: true
|
||||
};
|
||||
return npm.load(options, _.unary(callback));
|
||||
}, function(callback) {
|
||||
return npm.commands.update([packageJSON.name], function(error, data) {
|
||||
return callback(error, data);
|
||||
});
|
||||
}, function(data, callback) {
|
||||
var newVersion;
|
||||
if (_.isEmpty(data)) {
|
||||
return callback(new Error('You are already running the latest version'));
|
||||
}
|
||||
newVersion = _.last(_.first(_.last(data)).split('@'));
|
||||
console.info("Upgraded " + packageJSON.name + " to v" + newVersion + ".");
|
||||
return callback();
|
||||
}
|
||||
], done);
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
170
build/app.js
170
build/app.js
@ -1,170 +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.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 dataPrefix;
|
||||
dataPrefix = resin.settings.get('dataPrefix');
|
||||
return resin.data.prefix.set(dataPrefix, 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);
|
@ -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."
|
||||
}
|
||||
]
|
@ -1,45 +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.message != null) {
|
||||
console.error(error.message);
|
||||
}
|
||||
}
|
||||
if (_.isNumber(error.exitCode)) {
|
||||
errorCode = error.exitCode;
|
||||
} else {
|
||||
errorCode = 1;
|
||||
}
|
||||
if (exit) {
|
||||
return process.exit(errorCode);
|
||||
}
|
||||
};
|
||||
|
||||
}).call(this);
|
@ -1,68 +0,0 @@
|
||||
{
|
||||
"name": "resin-cli",
|
||||
"version": "0.0.1",
|
||||
"description": "Git Push to your devices",
|
||||
"main": "./lib/actions/index.coffee",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@bitbucket.org:rulemotion/resin-cli.git"
|
||||
},
|
||||
"preferGlobal": true,
|
||||
"bundled_engine": "v0.12.0",
|
||||
"man": [
|
||||
"./man/resin.1",
|
||||
"./man/resin-completion.1",
|
||||
"./man/resin-plugins.1"
|
||||
],
|
||||
"bin": {
|
||||
"resin": "./bin/resin"
|
||||
},
|
||||
"scripts": {
|
||||
"prepublish": "gulp build",
|
||||
"test": "gult test",
|
||||
"install": "node build/install-node.js bin/node"
|
||||
},
|
||||
"keywords": [
|
||||
"resin",
|
||||
"git"
|
||||
],
|
||||
"author": "Juan Cruz Viotti <juanchiviotti@gmail.com>",
|
||||
"license": "MIT",
|
||||
"optionalDependencies": {
|
||||
"windosu": "^0.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "~1.9.2",
|
||||
"gulp": "~3.8.9",
|
||||
"gulp-coffee": "^2.2.0",
|
||||
"gulp-coffeelint": "~0.4.0",
|
||||
"gulp-marked-man": "~0.3.1",
|
||||
"gulp-mocha": "~1.1.1",
|
||||
"gulp-shell": "^0.2.11",
|
||||
"gulp-util": "~3.0.1",
|
||||
"mocha": "~2.0.1",
|
||||
"mocha-notifier-reporter": "~0.1.0",
|
||||
"run-sequence": "~1.0.2",
|
||||
"sinon": "~1.12.1",
|
||||
"sinon-chai": "~2.6.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~0.9.0",
|
||||
"capitano": "~1.5.0",
|
||||
"coffee-script": "~1.8.0",
|
||||
"conf.js": "^0.1.1",
|
||||
"diskio": "^1.0.0",
|
||||
"drivelist": "^1.2.0",
|
||||
"git-cli": "~0.8.2",
|
||||
"lodash": "~2.4.1",
|
||||
"lodash-contrib": "~241.4.14",
|
||||
"mkdirp": "~0.5.0",
|
||||
"node-binary": "^1.0.1",
|
||||
"nplugm": "^2.0.0",
|
||||
"open": "0.0.5",
|
||||
"progress-stream": "^0.5.0",
|
||||
"resin-cli-visuals": "resin-io/resin-cli-visuals",
|
||||
"resin-sdk": "resin-io/resin-sdk",
|
||||
"underscore.string": "~2.4.0"
|
||||
}
|
||||
}
|
@ -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);
|
@ -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);
|
115
capitanodoc.coffee
Normal file
115
capitanodoc.coffee
Normal file
@ -0,0 +1,115 @@
|
||||
# coffeelint: disable=max_line_length
|
||||
|
||||
module.exports =
|
||||
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).
|
||||
|
||||
To get started download the CLI from npm.
|
||||
|
||||
$ npm install resin-cli -g
|
||||
|
||||
Then authenticate yourself:
|
||||
|
||||
$ resin login
|
||||
|
||||
Now you 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: 'Application'
|
||||
files: [ 'lib/actions/app.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'Authentication',
|
||||
files: [ 'lib/actions/auth.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'Device',
|
||||
files: [ 'lib/actions/device.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'Environment Variables',
|
||||
files: [ 'lib/actions/environment-variables.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: 'Sync',
|
||||
files: [ 'lib/actions/sync.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'SSH',
|
||||
files: [ 'lib/actions/ssh.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'Notes',
|
||||
files: [ 'lib/actions/notes.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'OS',
|
||||
files: [ 'lib/actions/os.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'Config',
|
||||
files: [ 'lib/actions/config.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'Preload',
|
||||
files: [ 'lib/actions/preload.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'Settings',
|
||||
files: [ 'lib/actions/settings.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'Wizard',
|
||||
files: [ 'lib/actions/wizard.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'Local',
|
||||
files: [ 'lib/actions/local/index.coffee' ]
|
||||
},
|
||||
{
|
||||
title: 'Deploy',
|
||||
files: [
|
||||
'lib/actions/build.coffee'
|
||||
'lib/actions/deploy.coffee'
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Utilities',
|
||||
files: [ 'lib/actions/util.coffee' ]
|
||||
},
|
||||
]
|
@ -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
|
@ -1,21 +0,0 @@
|
||||
# app create <name>
|
||||
|
||||
Use this command to create a new resin.io application.
|
||||
|
||||
You can specify the application 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
|
||||
|
||||
$ resin devices supported
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin app create MyApp
|
||||
$ resin app create MyApp --type raspberry-pi
|
||||
|
||||
## Options
|
||||
|
||||
### --type, -t <type>
|
||||
|
||||
application type
|
@ -1,7 +0,0 @@
|
||||
# app <id>
|
||||
|
||||
Use this command to show detailed information for a single application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin app 91
|
@ -1,10 +0,0 @@
|
||||
# init <id>
|
||||
|
||||
Use this command to associate a local project to an existing resin.io application.
|
||||
|
||||
The application should be a git repository before issuing this command.
|
||||
Notice this command adds a `resin` git remote to your application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ cd myApp && resin init 91
|
@ -1,10 +0,0 @@
|
||||
# apps
|
||||
|
||||
Use this command to list all your applications.
|
||||
|
||||
Notice this command only shows the most important bits of information for each app.
|
||||
If you want detailed information, use resin app <id> instead.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin apps
|
@ -1,17 +0,0 @@
|
||||
# app rm <id>
|
||||
|
||||
Use this command to remove a resin.io application.
|
||||
|
||||
Notice this command asks for confirmation interactively.
|
||||
You can avoid this by passing the `--yes` boolean option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin app rm 91
|
||||
$ resin app rm 91 --yes
|
||||
|
||||
## Options
|
||||
|
||||
### --yes, -y
|
||||
|
||||
confirm non interactively
|
@ -1,7 +0,0 @@
|
||||
# app restart <id>
|
||||
|
||||
Use this command to restart all devices that belongs to a certain application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin app restart 91
|
@ -1,22 +0,0 @@
|
||||
# login
|
||||
|
||||
Use this command to login to your resin.io account.
|
||||
You need to login before you can use most of the commands this tool provides.
|
||||
|
||||
You can pass your credentials as `--username` and `--password` options, or you can omit the
|
||||
credentials, in which case the tool will present you with an interactive login form.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin login --username <username> --password <password>
|
||||
$ resin login
|
||||
|
||||
## Options
|
||||
|
||||
### --username, -u <username>
|
||||
|
||||
user name
|
||||
|
||||
### --password, -p <password>
|
||||
|
||||
user password
|
@ -1,7 +0,0 @@
|
||||
# logout
|
||||
|
||||
Use this command to logout from your resin.io account.o
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin logout
|
@ -1,31 +0,0 @@
|
||||
# signup
|
||||
|
||||
Use this command to signup for a resin.io account.
|
||||
|
||||
If signup is successful, you'll be logged in to your new user automatically.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin signup
|
||||
Email: me@mycompany.com
|
||||
Username: johndoe
|
||||
Password: ***********
|
||||
|
||||
$ resin signup --email me@mycompany.com --username johndoe --password ***********
|
||||
|
||||
$ resin whoami
|
||||
johndoe
|
||||
|
||||
## Options
|
||||
|
||||
### --email, -e <email>
|
||||
|
||||
user email
|
||||
|
||||
### --username, -u <username>
|
||||
|
||||
user name
|
||||
|
||||
### --password, -p <user password>
|
||||
|
||||
user password
|
@ -1,7 +0,0 @@
|
||||
# whoami
|
||||
|
||||
Use this command to find out the current logged in username.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin whoami
|
112
doc/automated-init.md
Normal file
112
doc/automated-init.md
Normal 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.
|
1583
doc/cli.markdown
Normal file
1583
doc/cli.markdown
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,9 +0,0 @@
|
||||
# device identify <uuid>
|
||||
|
||||
Use this command to identify a device.
|
||||
|
||||
In the Raspberry Pi, the ACT led is blinked several times.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin device identify 23c73a12e3527df55c60b9ce647640c1b7da1b32d71e6a39849ac0f00db828
|
@ -1,7 +0,0 @@
|
||||
# device <id>
|
||||
|
||||
Use this command to show information about a single device.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin device 317
|
@ -1,46 +0,0 @@
|
||||
# device init [device]
|
||||
|
||||
Use this command to download the OS image of a certain application and write it to an SD Card.
|
||||
|
||||
Note that this command requires admin privileges.
|
||||
|
||||
If `device` is omitted, you will be prompted to select a device interactively.
|
||||
|
||||
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.
|
||||
|
||||
You need to configure the network type and other settings:
|
||||
|
||||
Ethernet:
|
||||
You can setup the device OS to use ethernet by setting the `--network` option to "ethernet".
|
||||
|
||||
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.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin device init --application 91 --network ethernet
|
||||
$ resin device init /dev/disk2 --application 91 --network wifi --ssid MyNetwork --key secret
|
||||
|
||||
## Options
|
||||
|
||||
### --application, --a,app, --a,app <application>
|
||||
|
||||
application id
|
||||
|
||||
### --network, -n <network>
|
||||
|
||||
network type
|
||||
|
||||
### --ssid, -s <ssid>
|
||||
|
||||
wifi ssid, if network is wifi
|
||||
|
||||
### --key, -k <key>
|
||||
|
||||
wifi key, if network is wifi
|
@ -1,13 +0,0 @@
|
||||
# devices
|
||||
|
||||
Use this command to list all devices that belong to a certain application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin devices --application 91
|
||||
|
||||
## Options
|
||||
|
||||
### --application, --a,app, --a,app <application>
|
||||
|
||||
application id
|
@ -1,17 +0,0 @@
|
||||
# device rm <id>
|
||||
|
||||
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 317
|
||||
$ resin device rm 317 --yes
|
||||
|
||||
## Options
|
||||
|
||||
### --yes, -y
|
||||
|
||||
confirm non interactively
|
@ -1,10 +0,0 @@
|
||||
# device rename <id> [name]
|
||||
|
||||
Use this command to rename a device.
|
||||
|
||||
If you omit the name, you'll get asked for it interactively.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin device rename 317 MyPi
|
||||
$ resin device rename 317
|
@ -1,7 +0,0 @@
|
||||
# devices supported
|
||||
|
||||
Use this command to get the list of all supported devices
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin devices supported
|
@ -1,7 +0,0 @@
|
||||
# drives
|
||||
|
||||
Use this command to list all drives that are connected to your machine.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin drives
|
@ -1,22 +0,0 @@
|
||||
# env add <key> [value]
|
||||
|
||||
Use this command to add an enviroment variable to an application.
|
||||
|
||||
You need to pass the `--application` option.
|
||||
|
||||
If value is omitted, the tool will attempt to use the variable's value
|
||||
as defined in your host machine.
|
||||
|
||||
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
|
||||
|
||||
## Options
|
||||
|
||||
### --application, --a,app, --a,app <application>
|
||||
|
||||
application id
|
@ -1,23 +0,0 @@
|
||||
# envs
|
||||
|
||||
Use this command to list all environment variables for a particular application.
|
||||
Notice we will support per-device environment variables soon.
|
||||
|
||||
This command lists all custom environment variables set on the devices running
|
||||
the application. If you want to see all environment variables, including private
|
||||
ones used by resin, use the verbose option.
|
||||
|
||||
Example:
|
||||
|
||||
$ resin envs --application 91
|
||||
$ resin envs --application 91 --verbose
|
||||
|
||||
## Options
|
||||
|
||||
### --application, --a,app, --a,app <application>
|
||||
|
||||
application id
|
||||
|
||||
### --verbose, -v
|
||||
|
||||
show private environment variables
|
@ -1,19 +0,0 @@
|
||||
# env rm <id>
|
||||
|
||||
Use this command to remove an environment variable from an application.
|
||||
|
||||
Don't remove resin specific variables, as things might not work as expected.
|
||||
|
||||
Notice this command asks for confirmation interactively.
|
||||
You can avoid this by passing the `--yes` boolean option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin env rm 215
|
||||
$ resin env rm 215 --yes
|
||||
|
||||
## Options
|
||||
|
||||
### --yes, -y
|
||||
|
||||
confirm non interactively
|
@ -1,7 +0,0 @@
|
||||
# env rename <id> <value>
|
||||
|
||||
Use this command to rename an enviroment variable from an application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin env rename 376 emacs
|
@ -1,10 +0,0 @@
|
||||
# example clone <id>
|
||||
|
||||
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
|
@ -1,7 +0,0 @@
|
||||
# example <id>
|
||||
|
||||
Use this command to show information of a single example application
|
||||
|
||||
Example:
|
||||
|
||||
$ resin example 3
|
@ -1,7 +0,0 @@
|
||||
# examples
|
||||
|
||||
Use this command to list available example applications from resin.io
|
||||
|
||||
Example:
|
||||
|
||||
$ resin examples
|
@ -1,8 +0,0 @@
|
||||
# help [command...]
|
||||
|
||||
Get detailed help for an specific command.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin help apps
|
||||
$ resin help os download
|
@ -1,3 +0,0 @@
|
||||
# version
|
||||
|
||||
Display the Resin CLI, as well as the bundled NodeJS version.
|
@ -1,11 +0,0 @@
|
||||
# key add <name> [path]
|
||||
|
||||
Use this command to associate a new SSH key with your account.
|
||||
|
||||
If `path` is omitted, the command will attempt
|
||||
to read the SSH key from stdin.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin key add Main ~/.ssh/id_rsa.pub
|
||||
$ cat ~/.ssh/id_rsa.pub | resin key add Main
|
@ -1,7 +0,0 @@
|
||||
# key <id>
|
||||
|
||||
Use this command to show information about a single SSH key.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin key 17
|
@ -1,7 +0,0 @@
|
||||
# keys
|
||||
|
||||
Use this command to list all your SSH keys.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin keys
|
@ -1,17 +0,0 @@
|
||||
# key rm <id>
|
||||
|
||||
Use this command to remove a SSH key from resin.io.
|
||||
|
||||
Notice this command asks for confirmation interactively.
|
||||
You can avoid this by passing the `--yes` boolean option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin key rm 17
|
||||
$ resin key rm 17 --yes
|
||||
|
||||
## Options
|
||||
|
||||
### --yes, -y
|
||||
|
||||
confirm non interactively
|
@ -1,18 +0,0 @@
|
||||
# note <|note>
|
||||
|
||||
Use this command to set or update a device note.
|
||||
|
||||
If note command isn't passed, the tool attempts to read from `stdin`.
|
||||
|
||||
To view the notes, use $ resin device <id>.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin note "My useful note" --device 317
|
||||
$ cat note.txt | resin note --device 317
|
||||
|
||||
## Options
|
||||
|
||||
### --device, --d,dev, --d,dev <device>
|
||||
|
||||
device id
|
@ -1,37 +0,0 @@
|
||||
# os download <id>
|
||||
|
||||
Use this command to download the device OS configured to a specific network.
|
||||
|
||||
Ethernet:
|
||||
You can setup the device OS to use ethernet by setting the `--network` option to "ethernet".
|
||||
|
||||
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.
|
||||
|
||||
By default, this command saved the downloaded image into a resin specific directory.
|
||||
You can save it to a custom location by specifying the `--output` option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin os download 91 --network ethernet
|
||||
$ resin os download 91 --network wifi --ssid MyNetwork --key secreykey123
|
||||
$ resin os download 91 --network ethernet --output ~/MyResinOS.zip
|
||||
|
||||
## Options
|
||||
|
||||
### --network, -n <network>
|
||||
|
||||
network type
|
||||
|
||||
### --ssid, -s <ssid>
|
||||
|
||||
wifi ssid, if network is wifi
|
||||
|
||||
### --key, -k <key>
|
||||
|
||||
wifi key, if network is wifi
|
||||
|
||||
### --output, -o <output>
|
||||
|
||||
output file
|
@ -1,34 +0,0 @@
|
||||
# os install <image> [device]
|
||||
|
||||
Use this command to write an operating system image to a device.
|
||||
|
||||
Note that this command requires admin privileges.
|
||||
|
||||
If `device` is omitted, you will be prompted to select a device interactively.
|
||||
|
||||
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
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin os install rpi.iso /dev/disk2
|
||||
|
||||
## Options
|
||||
|
||||
### --yes, -y
|
||||
|
||||
confirm non interactively
|
@ -1,9 +0,0 @@
|
||||
# plugin install <name>
|
||||
|
||||
Use this command to install a resin plugin
|
||||
|
||||
Use `--quiet` to prevent information logging.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin plugin install hello
|
@ -1,7 +0,0 @@
|
||||
# plugins
|
||||
|
||||
Use this command to list all the installed resin plugins.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin plugins
|
@ -1,17 +0,0 @@
|
||||
# plugin rm <name>
|
||||
|
||||
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
|
||||
|
||||
### --yes, -y
|
||||
|
||||
confirm non interactively
|
@ -1,9 +0,0 @@
|
||||
# plugin update <name>
|
||||
|
||||
Use this command to update a resin plugin
|
||||
|
||||
Use `--quiet` to prevent information logging.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin plugin update hello
|
@ -1,10 +0,0 @@
|
||||
# preferences
|
||||
|
||||
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
|
@ -1,15 +0,0 @@
|
||||
# update
|
||||
|
||||
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
|
@ -1,20 +0,0 @@
|
||||
mkdirp = require('mkdirp')
|
||||
fs = require('fs')
|
||||
path = require('path')
|
||||
capitano = require('capitano')
|
||||
markdown = require('./markdown')
|
||||
|
||||
capitano.command
|
||||
signature: 'markdown <file> <output>'
|
||||
description: 'file to markdown'
|
||||
action: (params, options, done) ->
|
||||
mkdirp.sync(params.output)
|
||||
action = require(params.file)
|
||||
|
||||
for actionName, actionCommand of action
|
||||
output = path.join(params.output, "#{actionName}.md")
|
||||
fs.writeFileSync(output, markdown.command(actionCommand))
|
||||
|
||||
return done()
|
||||
|
||||
capitano.run(process.argv)
|
@ -1,23 +0,0 @@
|
||||
_ = require('lodash')
|
||||
utils = require('./utils')
|
||||
|
||||
exports.option = (option) ->
|
||||
result = utils.parseSignature(option)
|
||||
|
||||
exports.command = (command) ->
|
||||
result = """
|
||||
# #{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}
|
||||
"""
|
||||
return result
|
46
extras/capitanodoc/index.coffee
Normal file
46
extras/capitanodoc/index.coffee
Normal file
@ -0,0 +1,46 @@
|
||||
_ = require('lodash')
|
||||
path = require('path')
|
||||
capitanodoc = require('../../capitanodoc')
|
||||
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(path.join(process.cwd(), file))
|
||||
|
||||
if actions.signature?
|
||||
category.commands.push(_.omit(actions, 'action'))
|
||||
else
|
||||
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
|
||||
anchor: '#' + 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))
|
66
extras/capitanodoc/markdown.coffee
Normal file
66
extras/capitanodoc/markdown.coffee
Normal file
@ -0,0 +1,66 @@
|
||||
_ = 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
|
@ -1,4 +1,5 @@
|
||||
_ = require('lodash')
|
||||
ent = require('ent')
|
||||
|
||||
exports.getOptionPrefix = (signature) ->
|
||||
if signature.length > 1
|
||||
@ -22,4 +23,4 @@ exports.parseSignature = (option) ->
|
||||
if option.parameter?
|
||||
result += " <#{option.parameter}>"
|
||||
|
||||
return result
|
||||
return ent.encode(result)
|
@ -1,12 +1,10 @@
|
||||
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 =
|
||||
@ -14,32 +12,20 @@ OPTIONS =
|
||||
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 'test', ->
|
||||
gulp.src(OPTIONS.files.tests, read: false)
|
||||
.pipe(mocha({
|
||||
reporter: mochaNotifierReporter.decorate('landing')
|
||||
}))
|
||||
|
||||
gulp.task 'coffee', [ 'test', 'lint', 'json' ], ->
|
||||
gulp.task 'coffee', [ 'lint' ], ->
|
||||
gulp.src(OPTIONS.files.app)
|
||||
.pipe(coffee())
|
||||
.pipe(gulp.dest(OPTIONS.directories.build))
|
||||
|
||||
gulp.task 'json', ->
|
||||
gulp.src(OPTIONS.files.json)
|
||||
.pipe(coffee(bare: true, header: true))
|
||||
.pipe(gulp.dest(OPTIONS.directories.build))
|
||||
|
||||
gulp.task 'lint', ->
|
||||
@ -49,11 +35,16 @@ gulp.task 'lint', ->
|
||||
}))
|
||||
.pipe(coffeelint.reporter())
|
||||
|
||||
gulp.task 'test', ->
|
||||
gulp.src(OPTIONS.files.tests, read: false)
|
||||
.pipe(mocha({
|
||||
reporter: 'min'
|
||||
}))
|
||||
|
||||
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' ])
|
||||
|
@ -1,7 +1,19 @@
|
||||
_ = 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')
|
||||
|
||||
exports.create =
|
||||
@ -10,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
|
||||
@ -26,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
|
||||
throw new Error('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'
|
||||
@ -53,64 +69,71 @@ exports.list =
|
||||
Use this command to list all your applications.
|
||||
|
||||
Notice this command only shows the most important bits of information for each app.
|
||||
If you want detailed information, use resin app <id> instead.
|
||||
If you want detailed information, use resin app <name> instead.
|
||||
|
||||
Examples:
|
||||
|
||||
$ 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 <id>'
|
||||
signature: 'app <name>'
|
||||
description: 'list a single application'
|
||||
help: '''
|
||||
Use this command to show detailed information for a single application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin app 91
|
||||
$ resin app MyApp
|
||||
'''
|
||||
permission: 'user'
|
||||
primary: true
|
||||
action: (params, options, done) ->
|
||||
resin.models.application.get params.id, (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 <id>'
|
||||
signature: 'app restart <name>'
|
||||
description: 'restart an application'
|
||||
help: '''
|
||||
Use this command to restart all devices that belongs to a certain application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin app restart 91
|
||||
$ resin app restart MyApp
|
||||
'''
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
resin.models.application.restart(params.id, done)
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
resin.models.application.restart(params.name).nodeify(done)
|
||||
|
||||
exports.remove =
|
||||
signature: 'app rm <id>'
|
||||
signature: 'app rm <name>'
|
||||
description: 'remove an application'
|
||||
help: '''
|
||||
Use this command to remove a resin.io application.
|
||||
@ -120,48 +143,15 @@ exports.remove =
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin app rm 91
|
||||
$ resin app rm 91 --yes
|
||||
$ resin app rm MyApp
|
||||
$ resin app rm MyApp --yes
|
||||
'''
|
||||
options: [ commandOptions.yes ]
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
visuals.patterns.remove 'application', options.yes, (callback) ->
|
||||
resin.models.application.remove(params.id, callback)
|
||||
, done
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
patterns = require('../utils/patterns')
|
||||
|
||||
exports.init =
|
||||
signature: 'init <id>'
|
||||
description: 'init an application'
|
||||
help: '''
|
||||
Use this command to associate a local project to an existing resin.io application.
|
||||
|
||||
The application should be a git repository before issuing this command.
|
||||
Notice this command adds a `resin` git remote to your application.
|
||||
|
||||
Examples:
|
||||
|
||||
$ cd myApp && resin init 91
|
||||
'''
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
currentDirectory = process.cwd()
|
||||
|
||||
async.waterfall [
|
||||
|
||||
(callback) ->
|
||||
resin.vcs.isResinProject(currentDirectory, callback)
|
||||
|
||||
(isResinProject, callback) ->
|
||||
if isResinProject
|
||||
error = new Error('Project is already a resin application.')
|
||||
return callback(error)
|
||||
return callback()
|
||||
|
||||
(callback) ->
|
||||
resin.models.application.get(params.id, callback)
|
||||
|
||||
(application, callback) ->
|
||||
resin.vcs.initProjectWithApplication(application, currentDirectory, callback)
|
||||
|
||||
], done
|
||||
patterns.confirm(options.yes, 'Are you sure you want to delete the application?').then ->
|
||||
resin.models.application.remove(params.name)
|
||||
.nodeify(done)
|
||||
|
@ -1,62 +1,124 @@
|
||||
_ = require('lodash-contrib')
|
||||
url = require('url')
|
||||
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.
|
||||
###
|
||||
|
||||
exports.login =
|
||||
signature: 'login'
|
||||
description: 'login to resin.io'
|
||||
help: '''
|
||||
Use this command to login to your resin.io account.
|
||||
You need to login before you can use most of the commands this tool provides.
|
||||
|
||||
You can pass your credentials as `--username` and `--password` options, or you can omit the
|
||||
credentials, in which case the tool will present you with an interactive login form.
|
||||
This command will prompt you to login using the following login types:
|
||||
|
||||
- Web authorization: open your web browser and prompt you to authorize the CLI
|
||||
from the dashboard.
|
||||
|
||||
- Credentials: using email/password and 2FA.
|
||||
|
||||
- Token: using the authentication token from the preferences page.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin login --username <username> --password <password>
|
||||
$ resin login
|
||||
$ resin login --web
|
||||
$ resin login --token "..."
|
||||
$ resin login --credentials
|
||||
$ resin login --credentials --email johndoe@gmail.com --password secret
|
||||
'''
|
||||
options: [
|
||||
{
|
||||
signature: 'username'
|
||||
parameter: 'username'
|
||||
description: 'user name'
|
||||
alias: 'u'
|
||||
signature: 'token'
|
||||
description: 'auth token'
|
||||
parameter: 'token'
|
||||
alias: 't'
|
||||
}
|
||||
{
|
||||
signature: 'password'
|
||||
parameter: 'password'
|
||||
description: 'user password'
|
||||
alias: 'p'
|
||||
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-preconfigured')
|
||||
auth = require('../auth')
|
||||
form = require('resin-cli-form')
|
||||
patterns = require('../utils/patterns')
|
||||
messages = require('../utils/messages')
|
||||
|
||||
hasOptionCredentials = not _.isEmpty(options)
|
||||
login = (options) ->
|
||||
if options.token?
|
||||
return Promise.try ->
|
||||
return options.token if _.isString(options.token)
|
||||
return form.ask
|
||||
message: 'Token (from the preferences page)'
|
||||
name: 'token'
|
||||
type: 'input'
|
||||
.then(resin.auth.loginWithToken)
|
||||
else if options.credentials
|
||||
return patterns.authenticate(options)
|
||||
else if options.web
|
||||
console.info('Connecting to the web dashboard')
|
||||
return auth.login()
|
||||
|
||||
if hasOptionCredentials
|
||||
return patterns.askLoginType().then (loginType) ->
|
||||
|
||||
if not options.username
|
||||
return done(new Error('Missing username'))
|
||||
if loginType is 'register'
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run)
|
||||
return capitanoRunAsync('signup')
|
||||
|
||||
if not options.password
|
||||
return done(new Error('Missing password'))
|
||||
options[loginType] = true
|
||||
return login(options)
|
||||
|
||||
async.waterfall [
|
||||
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 """
|
||||
|
||||
(callback) ->
|
||||
if hasOptionCredentials
|
||||
return callback(null, options)
|
||||
else
|
||||
return visuals.widgets.login(callback)
|
||||
Find out about the available commands by running:
|
||||
|
||||
(credentials, callback) ->
|
||||
resin.auth.login(credentials, callback)
|
||||
$ resin help
|
||||
|
||||
], done
|
||||
#{messages.reachingOut}
|
||||
"""
|
||||
.nodeify(done)
|
||||
|
||||
exports.logout =
|
||||
signature: 'logout'
|
||||
@ -70,7 +132,8 @@ exports.logout =
|
||||
'''
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
resin.auth.logout(done)
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
resin.auth.logout().nodeify(done)
|
||||
|
||||
exports.signup =
|
||||
signature: 'signup'
|
||||
@ -83,70 +146,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-preconfigured')
|
||||
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:
|
||||
|
||||
@ -154,9 +188,19 @@ exports.whoami =
|
||||
'''
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
resin.auth.whoami (error, username) ->
|
||||
Promise = require('bluebird')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
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)
|
||||
|
64
lib/actions/build.coffee
Normal file
64
lib/actions/build.coffee
Normal file
@ -0,0 +1,64 @@
|
||||
# Imported here because it's needed for the setup
|
||||
# of this action
|
||||
Promise = require('bluebird')
|
||||
dockerUtils = require('../utils/docker')
|
||||
|
||||
getBundleInfo = Promise.method (options) ->
|
||||
helpers = require('../utils/helpers')
|
||||
|
||||
if options.application?
|
||||
# An application was provided
|
||||
return helpers.getAppInfo(options.application)
|
||||
.then (app) ->
|
||||
return [app.arch, app.device_type]
|
||||
else if options.arch? and options.deviceType?
|
||||
return [options.arch, options.deviceType]
|
||||
else
|
||||
# No information, cannot do resolution
|
||||
return undefined
|
||||
|
||||
module.exports =
|
||||
signature: 'build [source]'
|
||||
description: 'Build a container locally'
|
||||
permission: 'user'
|
||||
help: '''
|
||||
Use this command to build a container with a 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).
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin build
|
||||
$ resin build ./source/
|
||||
$ resin build --deviceType raspberrypi3 --arch armhf
|
||||
$ 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 [
|
||||
{
|
||||
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) ->
|
||||
Logger = require('../utils/logger')
|
||||
dockerUtils.runBuild(params, options, getBundleInfo, new Logger())
|
||||
.asCallback(done)
|
||||
|
@ -1,15 +1,65 @@
|
||||
###
|
||||
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 =
|
||||
signature: 'yes'
|
||||
description: 'confirm non interactively'
|
||||
boolean: true
|
||||
alias: 'y'
|
||||
|
||||
exports.application =
|
||||
exports.optionalApplication =
|
||||
signature: 'application'
|
||||
parameter: 'application'
|
||||
description: 'application id'
|
||||
description: 'application name'
|
||||
alias: [ 'a', 'app' ]
|
||||
|
||||
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.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'
|
||||
@ -28,3 +78,23 @@ 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'
|
||||
|
311
lib/actions/config.coffee
Normal file
311
lib/actions/config.coffee
Normal file
@ -0,0 +1,311 @@
|
||||
###
|
||||
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.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) 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')
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run)
|
||||
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} #{uuid}"
|
||||
if options.advanced
|
||||
configureCommand += ' --advanced'
|
||||
return capitanoRunAsync(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.
|
||||
|
||||
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
|
||||
$ resin config generate --device 7cf02a6 --device-api-key <existingDeviceKey>
|
||||
$ resin config generate --device 7cf02a6 --output config.json
|
||||
$ resin config generate --app MyApp
|
||||
$ resin config generate --app MyApp --output config.json
|
||||
$ resin config generate --app MyApp --network wifi --wifiSsid mySsid --wifiKey abcdefgh --appUpdatePollInterval 1
|
||||
'''
|
||||
options: [
|
||||
commandOptions.optionalApplication
|
||||
commandOptions.optionalDevice
|
||||
commandOptions.optionalDeviceApiKey
|
||||
{
|
||||
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) ->
|
||||
Promise = require('bluebird')
|
||||
writeFileAsync = Promise.promisify(require('fs').writeFile)
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
_ = require('lodash')
|
||||
form = require('resin-cli-form')
|
||||
deviceConfig = require('resin-device-config')
|
||||
prettyjson = require('prettyjson')
|
||||
{ generateDeviceConfig, generateApplicationConfig } = require('../utils/config')
|
||||
|
||||
if not options.device? and not options.application?
|
||||
throw new Error '''
|
||||
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) ->
|
||||
if resource.uuid?
|
||||
generateDeviceConfig(resource, options.deviceApiKey, 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)
|
229
lib/actions/deploy.coffee
Normal file
229
lib/actions/deploy.coffee
Normal file
@ -0,0 +1,229 @@
|
||||
Promise = require('bluebird')
|
||||
dockerUtils = require('../utils/docker')
|
||||
|
||||
getBuilderPushEndpoint = (baseUrl, owner, app) ->
|
||||
querystring = require('querystring')
|
||||
args = querystring.stringify({ owner, app })
|
||||
"https://builder.#{baseUrl}/v1/push?#{args}"
|
||||
|
||||
getBuilderLogPushEndpoint = (baseUrl, buildId, owner, app) ->
|
||||
querystring = require('querystring')
|
||||
args = querystring.stringify({ owner, app, buildId })
|
||||
"https://builder.#{baseUrl}/v1/pushLogs?#{args}"
|
||||
|
||||
formatImageName = (image) ->
|
||||
image.split('/').pop()
|
||||
|
||||
parseInput = Promise.method (params, options) ->
|
||||
if not params.appName?
|
||||
throw new Error('Need an application to deploy to!')
|
||||
appName = params.appName
|
||||
image = undefined
|
||||
if params.image?
|
||||
if options.build or options.source?
|
||||
throw new Error('Build and source parameters are not applicable when specifying an image')
|
||||
options.build = false
|
||||
image = params.image
|
||||
else if options.build
|
||||
source = options.source || '.'
|
||||
else
|
||||
throw new Error('Need either an image or a build flag!')
|
||||
|
||||
return [appName, options.build, source, image]
|
||||
|
||||
showPushProgress = (message) ->
|
||||
visuals = require('resin-cli-visuals')
|
||||
progressBar = new visuals.Progress(message)
|
||||
progressBar.update({ percentage: 0 })
|
||||
return progressBar
|
||||
|
||||
getBundleInfo = (options) ->
|
||||
helpers = require('../utils/helpers')
|
||||
|
||||
helpers.getAppInfo(options.appName)
|
||||
.then (app) ->
|
||||
[app.arch, app.device_type]
|
||||
|
||||
performUpload = (imageStream, token, username, url, appName, logger) ->
|
||||
request = require('request')
|
||||
progressStream = require('progress-stream')
|
||||
zlib = require('zlib')
|
||||
|
||||
# Need to strip off the newline
|
||||
progressMessage = logger.formatMessage('info', 'Deploying').slice(0, -1)
|
||||
progressBar = showPushProgress(progressMessage)
|
||||
streamWithProgress = imageStream.pipe progressStream
|
||||
time: 500,
|
||||
length: imageStream.length
|
||||
, ({ percentage, eta }) ->
|
||||
progressBar.update
|
||||
percentage: Math.min(percentage, 100)
|
||||
eta: eta
|
||||
|
||||
uploadRequest = request.post
|
||||
url: getBuilderPushEndpoint(url, username, appName)
|
||||
headers:
|
||||
'Content-Encoding': 'gzip'
|
||||
auth:
|
||||
bearer: token
|
||||
body: streamWithProgress.pipe(zlib.createGzip({
|
||||
level: 6
|
||||
}))
|
||||
|
||||
uploadToPromise(uploadRequest, logger)
|
||||
|
||||
uploadLogs = (logs, token, url, buildId, username, appName) ->
|
||||
request = require('request')
|
||||
request.post
|
||||
json: true
|
||||
url: getBuilderLogPushEndpoint(url, buildId, username, appName)
|
||||
auth:
|
||||
bearer: token
|
||||
body: Buffer.from(logs)
|
||||
|
||||
uploadToPromise = (uploadRequest, logger) ->
|
||||
new Promise (resolve, reject) ->
|
||||
|
||||
handleMessage = (data) ->
|
||||
data = data.toString()
|
||||
logger.logDebug("Received data: #{data}")
|
||||
|
||||
try
|
||||
obj = JSON.parse(data)
|
||||
catch e
|
||||
logger.logError('Error parsing reply from remote side')
|
||||
reject(e)
|
||||
return
|
||||
|
||||
if obj.type?
|
||||
switch obj.type
|
||||
when 'error' then reject(new Error("Remote error: #{obj.error}"))
|
||||
when 'success' then resolve(obj)
|
||||
when 'status' then logger.logInfo("Remote: #{obj.message}")
|
||||
else reject(new Error("Received unexpected reply from remote: #{data}"))
|
||||
else
|
||||
reject(new Error("Received unexpected reply from remote: #{data}"))
|
||||
|
||||
uploadRequest
|
||||
.on('error', reject)
|
||||
.on('data', handleMessage)
|
||||
|
||||
module.exports =
|
||||
signature: 'deploy <appName> [image]'
|
||||
description: 'Deploy an image to a resin.io application'
|
||||
help: '''
|
||||
Use this command to deploy an image to an application, optionally building it first.
|
||||
|
||||
Usage: `deploy <appName> ([image] | --build [--source build-dir])`
|
||||
|
||||
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 --build --source myBuildDir/
|
||||
$ resin deploy myApp myApp/myImage
|
||||
'''
|
||||
permission: 'user'
|
||||
options: dockerUtils.appendOptions [
|
||||
{
|
||||
signature: 'build'
|
||||
boolean: true
|
||||
description: 'Build image then deploy'
|
||||
alias: 'b'
|
||||
},
|
||||
{
|
||||
signature: 'source'
|
||||
parameter: 'source'
|
||||
description: 'The source directory to use when building the image'
|
||||
alias: 's'
|
||||
},
|
||||
{
|
||||
signature: 'nologupload'
|
||||
description: "Don't upload build logs to the dashboard with image (if building)"
|
||||
boolean: true
|
||||
}
|
||||
]
|
||||
action: (params, options, done) ->
|
||||
_ = require('lodash')
|
||||
tmp = require('tmp')
|
||||
tmpNameAsync = Promise.promisify(tmp.tmpName)
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
|
||||
Logger = require('../utils/logger')
|
||||
logger = new Logger()
|
||||
|
||||
# Ensure the tmp files gets deleted
|
||||
tmp.setGracefulCleanup()
|
||||
|
||||
logs = ''
|
||||
|
||||
upload = (token, username, url) ->
|
||||
dockerUtils.getDocker(options)
|
||||
.then (docker) ->
|
||||
# Check input parameters
|
||||
parseInput(params, options)
|
||||
.then ([appName, build, source, imageName]) ->
|
||||
tmpNameAsync()
|
||||
.then (bufferFile) ->
|
||||
|
||||
# Setup the build args for how the build routine expects them
|
||||
options = _.assign({}, options, { appName })
|
||||
params = _.assign({}, params, { source })
|
||||
|
||||
Promise.try ->
|
||||
if build
|
||||
dockerUtils.runBuild(params, options, getBundleInfo, logger)
|
||||
else
|
||||
{ image: imageName, log: '' }
|
||||
.then ({ image: imageName, log: buildLogs }) ->
|
||||
logger.logInfo('Initializing deploy...')
|
||||
|
||||
logs = buildLogs
|
||||
Promise.all [
|
||||
dockerUtils.bufferImage(docker, imageName, bufferFile)
|
||||
token
|
||||
username
|
||||
url
|
||||
params.appName
|
||||
logger
|
||||
]
|
||||
.spread(performUpload)
|
||||
.finally ->
|
||||
# If the file was never written to (for instance because an error
|
||||
# has occured before any data was written) this call will throw an
|
||||
# ugly error, just suppress it
|
||||
Promise.try ->
|
||||
require('mz/fs').unlink(bufferFile)
|
||||
.catch(_.noop)
|
||||
.tap ({ image: imageName, buildId }) ->
|
||||
logger.logSuccess("Successfully deployed image: #{formatImageName(imageName)}")
|
||||
return buildId
|
||||
.then ({ image: imageName, buildId }) ->
|
||||
if logs is '' or options.nologupload?
|
||||
return ''
|
||||
|
||||
logger.logInfo('Uploading logs to dashboard...')
|
||||
|
||||
Promise.join(
|
||||
logs
|
||||
token
|
||||
url
|
||||
buildId
|
||||
username
|
||||
params.appName
|
||||
uploadLogs
|
||||
)
|
||||
.return('Successfully uploaded logs')
|
||||
.then (msg) ->
|
||||
logger.logSuccess(msg) if msg isnt ''
|
||||
.asCallback(done)
|
||||
|
||||
Promise.join(
|
||||
resin.auth.getToken()
|
||||
resin.auth.whoami()
|
||||
resin.settings.get('resinUrl')
|
||||
upload
|
||||
)
|
@ -1,133 +1,108 @@
|
||||
_ = require('lodash-contrib')
|
||||
path = require('path')
|
||||
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')
|
||||
osAction = require('./os')
|
||||
_ = require('lodash')
|
||||
|
||||
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 --application 91
|
||||
$ 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-preconfigured')
|
||||
visuals = require('resin-cli-visuals')
|
||||
|
||||
Promise.try ->
|
||||
if options.application?
|
||||
return resin.models.device.getAllByApplication(options.application)
|
||||
return resin.models.device.getAll()
|
||||
|
||||
.tap (devices) ->
|
||||
devices = _.map devices, (device) ->
|
||||
device.uuid = device.uuid.slice(0, 7)
|
||||
return device
|
||||
|
||||
console.log visuals.table.horizontal devices, [
|
||||
'id'
|
||||
'uuid'
|
||||
'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 <id>'
|
||||
signature: 'device <uuid>'
|
||||
description: 'list a single device'
|
||||
help: '''
|
||||
Use this command to show information about a single device.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin device 317
|
||||
$ resin device 7cf02a6
|
||||
'''
|
||||
permission: 'user'
|
||||
primary: true
|
||||
action: (params, options, done) ->
|
||||
resin.models.device.get params.id, (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'
|
||||
]
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
visuals = require('resin-cli-visuals')
|
||||
|
||||
return done()
|
||||
resin.models.device.get(params.uuid).then (device) ->
|
||||
|
||||
exports.remove =
|
||||
signature: 'device rm <id>'
|
||||
description: 'remove a device'
|
||||
help: '''
|
||||
Use this command to remove a device from resin.io.
|
||||
resin.models.device.getStatus(device).then (status) ->
|
||||
device.status = status
|
||||
|
||||
Notice this command asks for confirmation interactively.
|
||||
You can avoid this by passing the `--yes` boolean option.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin device rm 317
|
||||
$ resin device rm 317 --yes
|
||||
'''
|
||||
options: [ commandOptions.yes ]
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
visuals.patterns.remove 'device', options.yes, (callback) ->
|
||||
resin.models.device.remove(params.id, 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 <id> [name]'
|
||||
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 317 MyPi
|
||||
$ resin device rename 317
|
||||
'''
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
async.waterfall [
|
||||
|
||||
(callback) ->
|
||||
if not _.isEmpty(params.name)
|
||||
return callback(null, params.name)
|
||||
visuals.widgets.ask('How do you want to name this device?', callback)
|
||||
|
||||
(name, callback) ->
|
||||
resin.models.device.rename(params.id, name, callback)
|
||||
|
||||
], done
|
||||
console.log visuals.table.vertical 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'
|
||||
@ -139,72 +114,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-preconfigured')
|
||||
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.
|
||||
Note that device api keys are only supported on ResinOS 2.0.3+
|
||||
|
||||
If `device` is omitted, you will be prompted to select a device interactively.
|
||||
Examples:
|
||||
|
||||
$ resin device register MyApp
|
||||
$ resin device register MyApp --uuid <uuid>
|
||||
$ resin device register MyApp --uuid <uuid> --device-api-key <existingDeviceKey>
|
||||
'''
|
||||
permission: 'user'
|
||||
options: [
|
||||
{
|
||||
signature: 'uuid'
|
||||
description: 'custom uuid'
|
||||
parameter: 'uuid'
|
||||
alias: 'u'
|
||||
}
|
||||
commandOptions.optionalDeviceApiKey
|
||||
]
|
||||
action: (params, options, done) ->
|
||||
Promise = require('bluebird')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
|
||||
Promise.join(
|
||||
resin.models.application.get(params.application)
|
||||
options.uuid ? resin.models.device.generateUniqueKey()
|
||||
options.deviceApiKey ? resin.models.device.generateUniqueKey()
|
||||
(application, uuid, deviceApiKey) ->
|
||||
console.info("Registering to #{application.app_name}: #{uuid}")
|
||||
if not options.deviceApiKey?
|
||||
console.info("Using generated device api key: #{deviceApiKey}")
|
||||
else
|
||||
console.info('Using provided device api key')
|
||||
return resin.models.device.register(application.id, uuid, deviceApiKey)
|
||||
)
|
||||
.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) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
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.
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin device init --application 91 --network ethernet
|
||||
$ resin device init /dev/disk2 --application 91 --network wifi --ssid MyNetwork --key secret
|
||||
$ resin device identify 23c73a1
|
||||
'''
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
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) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
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) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
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) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
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) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
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) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
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) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
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) ->
|
||||
Promise = require('bluebird')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
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) ->
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
patterns = require('../utils/patterns')
|
||||
|
||||
resin.models.device.get(params.uuid).then (device) ->
|
||||
return options.application or patterns.selectApplication (application) ->
|
||||
return _.every [
|
||||
application.device_type is device.device_type
|
||||
device.application_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 MyApp
|
||||
'''
|
||||
options: [
|
||||
commandOptions.application
|
||||
commandOptions.network
|
||||
commandOptions.wifiSsid
|
||||
commandOptions.wifiKey
|
||||
commandOptions.optionalApplication
|
||||
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')
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run)
|
||||
rimraf = Promise.promisify(require('rimraf'))
|
||||
tmp = require('tmp')
|
||||
tmpNameAsync = Promise.promisify(tmp.tmpName)
|
||||
tmp.setGracefulCleanup()
|
||||
|
||||
params.id = options.application
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
helpers = require('../utils/helpers')
|
||||
patterns = require('../utils/patterns')
|
||||
|
||||
async.waterfall([
|
||||
Promise.try ->
|
||||
return options.application if options.application?
|
||||
return patterns.selectApplication()
|
||||
.then(resin.models.application.get)
|
||||
.then (application) ->
|
||||
|
||||
(callback) ->
|
||||
return callback(null, params.device) if params.device?
|
||||
visuals.patterns.selectDrive(callback)
|
||||
download = ->
|
||||
tmpNameAsync().then (tempPath) ->
|
||||
osVersion = options['os-version'] or 'default'
|
||||
capitanoRunAsync("os download #{application.device_type} --output '#{tempPath}' --version #{osVersion}")
|
||||
.disposer (tempPath) ->
|
||||
return rimraf(tempPath)
|
||||
|
||||
(device, callback) ->
|
||||
params.device = device
|
||||
visuals.patterns.confirm(options.yes, "This will completely erase #{params.device}. Are you sure you want to continue?", callback)
|
||||
Promise.using download(), (tempPath) ->
|
||||
capitanoRunAsync("device register #{application.app_name}")
|
||||
.then(resin.models.device.get)
|
||||
.tap (device) ->
|
||||
configureCommand = "os configure '#{tempPath}' #{device.uuid}"
|
||||
if options.config
|
||||
configureCommand += " --config '#{options.config}'"
|
||||
else if options.advanced
|
||||
configureCommand += ' --advanced'
|
||||
capitanoRunAsync(configureCommand)
|
||||
.then ->
|
||||
osInitCommand = "os initialize '#{tempPath}' --type #{application.device_type}"
|
||||
if options.yes
|
||||
osInitCommand += ' --yes'
|
||||
if options.drive
|
||||
osInitCommand += " --drive #{options.drive}"
|
||||
capitanoRunAsync(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
|
||||
|
||||
(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)
|
||||
|
@ -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()
|
@ -1,26 +1,41 @@
|
||||
_ = 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')
|
||||
|
||||
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.
|
||||
|
||||
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 +46,32 @@ exports.list =
|
||||
]
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
resin.models.environmentVariables.getAllByApplication options.application, (error, environmentVariables) ->
|
||||
return done(error) if error?
|
||||
Promise = require('bluebird')
|
||||
_ = require('lodash')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
visuals = require('resin-cli-visuals')
|
||||
|
||||
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
|
||||
throw new Error('You must specify an application or device')
|
||||
|
||||
.tap (environmentVariables) ->
|
||||
if _.isEmpty(environmentVariables)
|
||||
throw new Error('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 +84,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 +114,46 @@ exports.add =
|
||||
help: '''
|
||||
Use this command to add an enviroment variable to an application.
|
||||
|
||||
You need to pass the `--application` option.
|
||||
|
||||
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]
|
||||
Promise = require('bluebird')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
|
||||
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
|
||||
throw new Error('You must specify an application or device')
|
||||
.nodeify(done)
|
||||
|
||||
exports.rename =
|
||||
signature: 'env rename <id> <value>'
|
||||
@ -106,10 +161,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)
|
||||
|
@ -1,102 +0,0 @@
|
||||
async = require('async')
|
||||
fs = require('fs')
|
||||
path = require('path')
|
||||
_ = require('lodash')
|
||||
gitCli = require('git-cli')
|
||||
resin = require('resin-sdk')
|
||||
visuals = require('resin-cli-visuals')
|
||||
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}"))
|
||||
|
||||
async.waterfall [
|
||||
|
||||
(callback) ->
|
||||
exampleAbsolutePath = path.join(process.cwd(), example.name)
|
||||
|
||||
fs.exists exampleAbsolutePath, (exists) ->
|
||||
return callback() if not exists
|
||||
error = new Error("Directory exists: #{example.name}")
|
||||
return callback(error)
|
||||
|
||||
(callback) ->
|
||||
console.info("Cloning #{example.display_name} to #{example.name}")
|
||||
gitCli.Repository.clone(example.repository, example.name, callback)
|
||||
|
||||
], done
|
@ -1,115 +1,101 @@
|
||||
###
|
||||
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')
|
||||
|
||||
# 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.plugin
|
||||
return 'plugins'
|
||||
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
|
||||
if not _.isEmpty(groupedCommands.plugins)
|
||||
console.log('\nInstalled plugins:\n')
|
||||
print(parse(groupedCommands.plugins))
|
||||
|
||||
for option in options
|
||||
console.log(getOptionHelp(option, optionSignatureMaxLength))
|
||||
console.log('\nAdditional commands:\n')
|
||||
print(parse(groupedCommands.secondary))
|
||||
else
|
||||
console.log('\nRun `resin help --verbose` to list additional commands')
|
||||
|
||||
console.log()
|
||||
if not _.isEmpty(capitano.state.globalOptions)
|
||||
console.log('\nGlobal Options:\n')
|
||||
print(parse(capitano.state.globalOptions))
|
||||
|
||||
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)
|
||||
return done(new Error("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 +110,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)
|
||||
|
@ -1,16 +1,38 @@
|
||||
###
|
||||
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')
|
||||
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')
|
||||
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')
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user