tag:blogger.com,1999:blog-31340182673779168512018-05-22T03:01:20.116-05:00adb -d shell reboot recoveryRyan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.comBlogger106125tag:blogger.com,1999:blog-3134018267377916851.post-32480139649276133172018-01-06T19:13:00.001-06:002018-01-06T19:14:59.057-06:00Marlin Firmware and Jenkins<p>I have a 3d printer that uses the <a href="https://github.com/MarlinFirmware/Marlin" target="_blank">Marlin firmware</a>. It’s an Alunar M508 (Prusa I3 Clone) and I maintain <a href="https://github.com/camalot/alunar-prusa-i3-marlin-i3-firmware" target="_blank">a build of Marlin</a> for it that is tailored for that printer. Recently, I thought it would be fun to set up a Jenkins build agent that can build Arduino code. The agent is a docker image that is based off a base sshd image. Currently these are not available publicly, but I may change that in the future.</p><p>The base image is just a standard sshd server so so Jenkins can communicate to the agent. </p><p>The arduino agent installs arduino 1.8.5, and a few things like pip, gcc, etc. I also have some scripts that I put together that allow the build pipeline install Arduino Boards and Libraries. Like the board and library managers in the UI. </p><p><code>board-manager --name='Arduino AVR Boards' --version='latest' --packager='arduino'</code></p><p>That command will install the latest AVR boards and all the required dependencies. Additionally, to install a libarary, it would look like this:</p><p><code>library-manager --name='LiquidCrystal' --version='latest';</code></p><p>After I got all that setup and able to output the hex binaries from the arduino-builder, I thought it would be fun if I could trust that the build that came out flashes to a device successfully. I pulled out the extra Mega2560 board I have that I use for testing my builds of the firmware anyhow before I would flash it to the printer, and I plugged it in to my Docker Host. The Jenkins Arduino agent mounts that device so it can access it.</p><p>After the binaries are built, I run:</p><p><code>avrdude -p m2560 -c avrispmkII -P /dev/ttyACM0 -C /usr/local/etc/avrdude.conf -D -U flash:w:/path/to/build.hex:i</code></p><p>Which will write the binary to the device. After that is successful, I run some serial commands to actually read from the Firmware to validate that it is announcing / working as Marlin.<p>After that is successful, it will zip up the hex files, and it creates a Github release and uploads the file to the release.<p>You can find most of the “work” in the github repo in either the Jenkinsfile, or the .deploy scripts folder. I still have some issues to iron out, like making sure the serial port is available again after the push to the device. After I get that stuff wrapped up, I will see what I have to adjust to make the container images publicly available.</p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/XHIZy9soHBc" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2018/01/marlin-firmware-and-jenkins.htmltag:blogger.com,1999:blog-3134018267377916851.post-19092375692751060302017-12-29T09:39:00.001-06:002017-12-29T09:39:46.714-06:00Setting up Artifactory as Docker Registry<p>I was setting up artifactory as a docker registry on-premises with a self-signed certificate. This was not as simple as some of the docs suggested. It took me a bit to put together the process for this as it wasn’t really laid out in any single place. Here is what I did to get it working.</p><p>Distro: Ubuntu 16.04</p><p>I decided to do the subdomain method for setup. my FQDN that I will be subdomaining off of is <code>artifactory.contoso.com</code>. Each subdomain will be a different registry within artifactory. This will assume you already have an NGINX&nbsp; instance setup to do the reverse proxy with the configuration defined by the Artifactory Reverse Proxy Generator.</p><p>Create self-signed certificate. I store mine in <code>/mnt/data/ssl</code></p><pre><code>$ openssl req -newkey rsa:2048 -nodes –keyout /mnt/data/ssl/wildcard.artifactory.contoso.com.key -x509 -days 365 –out /mnt/data/ssl/wildcard.artifactory.contoso.com.cert</code></pre><p>Need to make this certificate available for docker</p><p><pre><code># mkdir –p /etc/docker/certs.d/wildcard.artifactory.contoso.com;<br># cp /mnt/data/ssl/wildcard.artifactory.contoso.com.key /etc/docker/certs.d/wildcard.artifactory.contoso.com/domain.key;<br># cp /mnt/data/ssl/wildcard.artifactory.contoso.com.cert /etc/docker/certs.d/wildcard.artifactory.contoso.com/domain.cert;<code><br># ln –s /etc/docker/certs.d/wildcard.artifactory.contoso.com /etc/docker/certs.d/docker.artifactory.contoso.com; <br># ln –s /etc/docker/certs.d/wildcard.artifactory.contoso.com /etc/docker/certs.d/docker-local.artifactory.contoso.com;</code></code></pre><p>Now we have a folder setup for each subdomain for each docker registry in Artifactory. Next we need to add the certificates so the CA is known by the system.</p><p><pre><code># cp /mnt/data/ssl/wildcard.artifactory.contoso.com.key /usr/local/share/ca-certificates/wildcard.artifactory.contoso.com.key;<br># cp /mnt/data/ssl/wildcard.artifactory.contoso.com.cert /usr/local/share/ca-certificates/wildcard.artifactory.contoso.com.crt;<br># update-ca-certificates;</code></pre><p>Next we need to add the domains to the docker options to allow them to be insecure.</p><pre><code># nano /etc/init.d/docker<br>### EDIT ###<br>DOCKER_OPTS="$DOCKER_OPTS --insecure-registry docker.artifactory.contoso.com --insecure-registry docker-local.artifactory.contoso.com</code></pre><p>Finally, we just need to restart docker.</p><p><pre><code># systemctl restart docker</code></pre><p>YMMV, but these are the steps that I needed to do to get things working for me.</p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/lYjgQEC9Kgk" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/12/setting-up-artifactory-as-docker.htmltag:blogger.com,1999:blog-3134018267377916851.post-27785438052183170802017-12-13T18:36:00.001-06:002017-12-13T18:36:14.782-06:00No Matching Cipher Found<p>Today I tried to pull latest from the <code>develop</code> branch in a <code>git</code> repository in TFS 2015. I use SSH for authentication to tfs git repositories, and when I ran the <code>git pull</code> command, I was presented with the following error:</p> <pre><code>no matching cipher found. their offer: aes256-cbc,aes192-cbc,aes128-cbc<br /></code></pre> <p>There were some other lines about making sure the repository existed, and that I had permission, etc. But this line was the one that sort of stood out to me. It is not an error that I have come across before. It took me a little while to track down the issue, which is why I am writing this. </p> <p>The error is not a TFS issue, nor is it a git issue. The error is coming from <code>SSH</code>. I think it started after I updated my version of <code>openSSH</code> on my mac to version <code>7.6p1</code>.</p> <p>To fix the issue, I opened up <code>/etc/ssh/ssh_config</code> and added the lines:</p> <pre><code>Match Host my-tfs-server.company-domain.com<br /> Ciphers +aes128-cbc,aes192-cbc,aes256-cbc<br /></code></pre> <p>You could make it less restrictive and omit <code>Match Host</code> line altogether, but I would rather add the exception for the specific servers that require it. After adding those lines, I was able to pull latest again.</p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/fv3hEIIFUdg" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/12/no-matching-cipher-found.htmltag:blogger.com,1999:blog-3134018267377916851.post-7706972444661022362017-11-26T19:24:00.001-06:002017-11-26T19:25:22.764-06:00ROLLS HA43 PRO Monitor Post Mount<p>I have a <a href="https://smile.amazon.com/rolls-HA43PRO-CH-Headphone-Amp/dp/B00102ZOQC/ref=sr_1_2?ie=UTF8&amp;qid=1511744950&amp;sr=8-2&amp;keywords=ROLLS+HA43+pro" target="_blank">ROLLS HA43 PRO 4 Channel Headphone amplifier</a> and I have hated how it sits on my desk. It never stays where I want it, and is a pain to see what the volume is set to. </p><p>This weekend, I took some time in Autodesk Fusion 360 to design a tray to mount the HA43 to my monitor post. After a couple of test prints, and filament spool changes, it is finally printed. </p><p>If you have this amp, and a 2" (51mm) monitor post, you can download the files from <a href="https://www.thingiverse.com/thing:2671004" target="_blank">Thingiverse</a>.</p><p><a href="https://lh3.googleusercontent.com/-HcnZez2t458/WhtpPGWgzZI/AAAAAAABAfQ/F4EnoZcd-jIAXkW9FGJL-zj-ogpQW-VpACHMYCw/s1600-h/2017-11-26%2B17.59.46%255B3%255D" target="_blank"><img width="240" height="180" title="2017-11-26 17.59.46" style="border: 0px currentcolor; border-image: none; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="2017-11-26 17.59.46" src="https://lh3.googleusercontent.com/-NFtmhhU6SXc/WhtpQN0AIUI/AAAAAAABAfU/D0SsbQpVNb0K4v759Y9sda5k_028RHNMQCHMYCw/2017-11-26%2B17.59.46_thumb?imgmax=800" border="0"></a><a href="https://lh3.googleusercontent.com/-045pD2i7JDw/WhtpRMvWUrI/AAAAAAABAfY/Hy7I2SChjj0hmCMDQ_PAVaTu--b9VME_QCHMYCw/s1600-h/2017-11-26%2B18.41.07%255B3%255D" target="_blank"><img width="240" height="180" title="2017-11-26 18.41.07" style="border: 0px currentcolor; border-image: none; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="2017-11-26 18.41.07" src="https://lh3.googleusercontent.com/-6IDcuF1JO98/WhtpSRTnspI/AAAAAAABAfc/wBKR1Ci3U6ACEKOPgWDDHdt7S0Yz6y6ggCHMYCw/2017-11-26%2B18.41.07_thumb?imgmax=800" border="0"></a><a href="https://lh3.googleusercontent.com/-V34JXGQRmCw/WhtpTavo7QI/AAAAAAABAfg/iYv6529sEpUzWr-e5vBS9VNWtAL7uZaAACHMYCw/s1600-h/2017-11-26%2B18.41.26%255B3%255D" target="_blank"><img width="240" height="180" title="2017-11-26 18.41.26" style="border: 0px currentcolor; border-image: none; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="2017-11-26 18.41.26" src="https://lh3.googleusercontent.com/-hwyHVKndD7A/WhtpURziwPI/AAAAAAABAfk/yAkelQoQDTM-y702-BdxN6Wd3P2tPKvHACHMYCw/2017-11-26%2B18.41.26_thumb?imgmax=800" border="0"></a><a href="https://lh3.googleusercontent.com/-TSNuyjXdhWc/WhtpVoE0pcI/AAAAAAABAfo/kksvoF1ZA30xSKCQoHSs_diyKbKLcdBtgCHMYCw/s1600-h/2017-11-26%2B18.42.03%255B3%255D" target="_blank"><img width="240" height="180" title="2017-11-26 18.42.03" style="border: 0px currentcolor; border-image: none; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="2017-11-26 18.42.03" src="https://lh3.googleusercontent.com/-xdWlhxUD-5c/WhtpWqUWKKI/AAAAAAABAfs/Zgfd19LcDw05zZ0X2ML0epUiF9LU9cFzgCHMYCw/2017-11-26%2B18.42.03_thumb?imgmax=800" border="0"></a></p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/ertv_pwZv2c" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/11/rolls-ha43-pro-monitor-post-mount.htmltag:blogger.com,1999:blog-3134018267377916851.post-86990351059023546502017-10-20T13:08:00.001-05:002017-10-20T13:08:17.394-05:00PAGE FAULT IN UNPAGED AREA Windows 10 Insider Build 17017 GSOD/BSOD<p>After updating to Windows Insider 17017, I rebooted. Once I did, I got stuck in a GSOD/BSOD boot loop. See the loop in this video:</p><p><a href="https://youtu.be/DtozUUJ7Ze8" target="_blank"><img width="640" height="360" src="http://i3.ytimg.com/vi/DtozUUJ7Ze8/maxresdefault.jpg"></a></p><p>After some time looking around and such, I found that I needed to copy the <code>volsnap.sys</code> file from the <code>Windows.old</code> folder to the current <code>Windows</code> folder. </p><p>Here are the steps I took:</p><ul><li>Downloaded the <a href="https://www.microsoft.com/en-us/software-download/windows10" target="_blank">Windows Media Creation Tool</a>. </li><li>Booted off the USB created above.</li><li>Went into the Recovery tools for the system and opened the command console</li><li><code>move c:\Windows\System32\drivers\volsnap.sys c:\Windows\System32\drivers\volsnap.bkp</code></li><li><code>copy c:\Windows.old\Windows\System32\drivers\volsnap.sys c:\Windows\System32\drivers\volsnap.sys</code></li><li>reboot</li></ul><p>System then booted up with no problem.</p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/8PNZrCwlivQ" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/10/page-fault-in-unpaged-area-windows-10.htmltag:blogger.com,1999:blog-3134018267377916851.post-69334902111353875782017-09-23T12:19:00.001-05:002017-09-23T12:19:48.716-05:00User Docker Agents with Jenkins<p>I am running Jenkins in my home lab in docker container. I wanted to also have different agents for the different types of projects I was building, like building some of my Arduino projects for the different boards I have, or nodejs packages, etc.</p><p>I have the Jenkins Cloud Plugin and docker-plugin installed. I was having some issues configuring it to connect to the docker api, and the plugin documentation is out dated.</p><p>Since this is my lab, I just set up for TCP communication vs the <code>docker.sock</code>. The documentation says to edit the <code>/etc/init/docker.conf</code>. Except on Ubuntu 16.04 that is not used. </p><p>Instead, you have to edit <code>/lib/systemd/system/docker.service</code> and add <code>-H 0.0.0.0:32376</code> to <code>ExecStart</code>. </p><p>Run <code>systemctl daemon-reload</code></p><p>And finally, <code>systemctl restart docker</code></p><p>Now docker engine will restart and it will be listening on port 32376. If you do <code>curl –XGET http://localhost:32376</code> you should get a response.</p><p>You can now configure jenkins to use <code>tcp://&lt;docker-host&gt;:32376</code> and hit test connection.</p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/oIBx6gO_pTE" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/09/user-docker-agents-with-jenkins.htmltag:blogger.com,1999:blog-3134018267377916851.post-80703620965980703502017-08-10T19:51:00.001-05:002017-08-10T19:51:19.587-05:00Diabetes Update<p><a href="http://blog.twimager.com/2017/01/type-2-diabetes-diagnosis.html" target="_blank">Back in January</a> I talked about my Type 2 Diabetes diagnosis. It is now 7 months later, and I am proud to say that I have reversed my Type 2 Diabetes.</p><p>I am still using the Diabetes:M app that I discussed before, to track my daily calorie/carb intake.</p><p>I changed my diet, I did not just continue to eat as I did before and rely upon the insulin to correct my glucose. I started off by changing to a more of a restricted carb diet, this was the recommendation from the Dr. at the hospital. This consisted of eating fewer carbs, but not what I would consider a drastic change. It was a restriction of 75g of carbs per meal. </p><p>A few weeks in to January, I adjusted even more. I went to more of a <a href="https://en.wikipedia.org/wiki/Ketogenic_diet" target="_blank">Ketogenic</a> style, or <a href="https://en.wikipedia.org/wiki/Ketogenic_diet#Modified_Atkins" target="_blank">Atkins Modified</a>, diet. Typically, I consume fewer than 40g of carbs per day, which is not as restrictive as either of these diets, but it is what I am currently following. When I started doing this diet, I didn't even know what a Ketogenic diet was. This was just a restriction that I put upon myself. My meals consist of high protein, high fat, low carb items. My go to is Potbelly's Farmhouse Salad (sans the tomatoes, because I don't like them) with the Potbelly Vinaigrette Dressing. This is about 560 calories, and 14g carbs. Or I will have the Jimmy John's #15 Tuna Unwich (again, no tomatoes). About 650 calories, and 9g carbs. But I also go to places like 5 Guys and get the Bacon Cheeseburger… The key is to axe the bread, and fries. I eat a lot fewer calories than I used to as well. On average, I consume 1,350 calories per day. </p><p>A lot of this change in my diet stemmed from an episode of <a href="https://www.youtube.com/watch?v=PhHtBqsGAoA" target="_blank">The Joe Rogan Experience with guest Neil DeGrasse Tyson</a>. In the episode, Neil said "If a physicist wrote a diet book it would be one line: Consume fewer calories than you burn". That sentence made complete sense to me. It is so simple. So that is what I did. Also in that episode, they talked about how Terry Crews does intermittent fasting. He only eats within an 8 hour window of the day. I also adapted that practice. I eat lunch at about 11:30am (or 12:00pm) and then I have dinner at about 6:30pm. I may have a snack of like a SlimJim type of food in between. But after 8:00pm I do not eat again, until lunch the next day.</p><p>Back in December, when I was admitted to the hospital, I weighed in at 347lbs. And this was after about a week or 2 of me not really eating because I was not feeling well. As of this morning, the day of writing this, I am 265.6lbs. That is 81.4lbs lost in 7 months, for those that are counting. Keep in mind, everything I described above is dietary changes. I have not added really any exercise to my day, except maybe a little bit of walking. The next phase of this will be to work in exercise to my daily routine. </p><p>This is just the beginning of my story. This is not the end. This is not something that I can say "Oh, I can eat what ever I want again, I am not a diabetic any more". </p><p>I never liked to share pictures of myself, because I was not happy with my appearance, while I am not satisfied to call it done, I will say that I am a lot happier with how I look, and feel. The picture on the left is back from October 2015 (It was the most recent photo I could find), the picture on the right is from the end of July 2017.</p><p><a href="https://lh3.googleusercontent.com/-t-aM0nTJWXM/WYz_gSBxPLI/AAAAAAAA80s/MGJ7rTNx1F06QqGjrp_j_RSR8AiWuUnBQCHMYCw/s1600-h/DFZNQY4XkAAsfgr%255B5%255D"><img width="505" height="449" title="DFZNQY4XkAAsfgr" style="border: 0px currentcolor; border-image: none; margin-right: auto; margin-left: auto; float: none; display: block; background-image: none;" alt="DFZNQY4XkAAsfgr" src="https://lh3.googleusercontent.com/-NSu5cvJiH_0/WYz_hmxQAEI/AAAAAAAA80w/LIwMy3TFT2kse9gKK8lAApkjZ5trCF0lgCHMYCw/DFZNQY4XkAAsfgr_thumb%255B3%255D?imgmax=800" border="0"></a></p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/CcuWXRGZoUg" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/08/diabetes-update.htmltag:blogger.com,1999:blog-3134018267377916851.post-27986729548401185692017-08-10T19:06:00.001-05:002017-08-10T20:54:58.043-05:00Using Jenkins to rotate AWS Access Key for Jenkins User<p>At my work, we use Jenkins to handle the workloads for CI/CD to AWS. We have a credential stored in Jenkins that can assume a role to perform some tasks based on the application being built/deployed. This credential is an IAM user that uses an Access Key &amp; Secret to authenticate and has only CLI access. We rotate the key for this account frequently. </p><p>Rotating the key helps keep a couple things in line: </p><ol><li>We have a security policy that any IAM User that uses access keys has the keys rotated. This is also a best practice any how, with any password…</li><li>Since we rotate the key, we know that only the jenkins credential is what has the correct access key, so if someone manages to get the credential, and they are using it, even if their intention is sound, this will ensure that the 'rogue' service using it will not function after the key is rotated. </li></ol><p>The other day was the day that the key had to be rotated, and I took on that task. We had no automation around this, so I manually updated the access key. And after I updated the new access key, In my spare time, I started to write some automation so this no longer has to be done manually in the future. </p><p>Here is the flow of the process that will happen during the key rotation:</p><ul><li>Jenkins will check the created date of the current access key</li><li>If the date is older than the Expiration date</li><ul><li>The key will be set to inactive</li><li>A new key will be generated</li><li>The jenkins credential will be updated with this information</li><li>The inactive key will be deleted</li></ul><li>If any of those steps fail, it will trigger the rollback, which will set the current key as active again, and the job will be marked as FAILED.</li></ul><p>The first part of this process is the <code>rotate.sh</code> script (do not judge my bash script… I do not claim to be an expert)</p><div class="gistLoad" id="gist-323652f344edcd867451582b0d2aa929" data-file="rotate.sh" data-id="323652f344edcd867451582b0d2aa929"><pre>#!/usr/bin/env bash<br /><br />set -e;<br />function print_usage() {<br /> (&gt;&amp;2 echo -e "Usage $0 -i <jenkins_credential_id> -u <aws_iam_username>\n");<br /> (&gt;&amp;2 echo -e "-i:\tThe unique identifier for the jenkins credential");<br /> (&gt;&amp;2 echo -e "-u:\tThe AWS IAM username to check the key for");<br /> exit 1;<br />}<br /><br />function process_key() {<br /> local kuser=$1;<br /> local kdate=$(date -d $2 +%s);<br /> local kkey=$3;<br /> local exp_date=$(date -d "now - 90 days" +%s);<br /><br /> if [ $kdate -le $exp_date ]; then<br /> echo "Credential '$kkey' is expired and will be rotated.";<br /> # get new key<br /> local create_call=$(aws iam create-access-key --user-name "$kuser");<br /> # this is for testing<br /> # local create_call=$(cat create.json);<br /><br /> local new_access_key=$(echo $create_call | jq -r '.AccessKey.AccessKeyId');<br /> local new_secret_key=$(echo $create_call | jq -r '.AccessKey.SecretAccessKey');<br /><br /> # set old key inactive<br /> aws iam update-access-key --access-key-id "$kkey" --status Inactive --user-name "$kuser";<br /><br /> export CI_ROTATE_CREDENTIAL_ID=$credential_id;<br /> export CI_ROTATE_OLD_ACCESS_KEY_ID=$kkey;<br /> export CI_ROTATE_NEW_ACCESS_KEY_ID="$new_access_key";<br /> export CI_ROTATE_NEW_SECRET_KEY="$new_secret_key";<br /><br /> echo "Update jenkins credential with the newly created AccessKey.";<br /> groovy "./set-credential.groovy";<br /> # this is for testing<br /> # groovy "./dummy.groovy"<br /><br /> # delete old key since this was successful.<br /> aws iam delete-access-key --access-key-id "$kkey" --user-name "$kuser";<br /><br /> export CI_ROTATE_NEW_SECRET_KEY="";<br /> export CI_ROTATE_NEW_ACCESS_KEY_ID="";<br /> export CI_ROTATE_OLD_ACCESS_KEY_ID="";<br /> export CI_ROTATE_CREDENTIAL_ID="";<br /> else<br /> echo "Credential '$kkey' was not rotated because it is not expired.";<br /> fi<br /><br />}<br /><br />function roll_back() {<br /> kusername=$1;<br /> kaccesskey=$2;<br /><br /> if [ -z "${kusername}" ] || [ -z "${kaccesskey}" ]; then<br /> (&gt;&amp;2 echo "FATAL: Missing required values to rollback.");<br /> exit 1;<br /> fi<br /><br /> ## Set the original key back to active<br /> aws iam update-access-key --access-key-id $kaccesskey --status Active --user-name $kusername;<br /><br /> (&gt;&amp;2 echo "There was a failure and the access key ($kaccesskey) was set back to the Active state.");<br /> # This will always exit 1 because if we come here we are in a failure state.<br /> exit 1;<br />}<br /><br />function run_rotate() {<br /> while getopts "i:u:r:" arg; do<br /> case $arg in<br /> i)<br /> local credential_id=$OPTARG;<br /> ;;<br /> u)<br /> local user_account=$OPTARG;<br /> ;;<br /> r)<br /> local rotate_region=$OPTARG;<br /> ;;<br /> esac<br /> done<br /> shift $((OPTIND-1))<br /><br /> if [ -z "${user_account}" ] || [ -z "${credential_id}" ]; then<br /> print_usage;<br /> fi<br /><br /> if ! command -v groovy &gt; /dev/null 2&gt;&amp;1; then<br /> (&gt;&amp;2 echo "Unable to locate required groovy command. You must have groovy installed to run the key rotation script.");<br /> exit 1;<br /> if<br /><br /> # https://aws.amazon.com/blogs/security/how-to-rotate-access-keys-for-iam-users/<br /> local current_keys=$(aws iam list-access-keys --user-name "${user_account}" | jq -r '.AccessKeyMetadata[] | "\(.UserName),\(.CreateDate),\(.AccessKeyId)"');<br /><br /> # this is for testing<br /> # local current_keys=$(cat keys.json | jq -r '.AccessKeyMetadata[] | "\(.UserName),\(.CreateDate),\(.AccessKeyId)"');<br /><br /> for key in $current_keys; do<br /> # split by ',' and store<br /> IFS=, read xusername xcreatedate xaccesskey &lt;&lt;&lt; $key;<br /> process_key "$xusername" "$xcreatedate" "$xaccesskey" || roll_back "$xusername" "$xaccesskey";<br /> done<br /><br /> echo "Jenkins / AWS Credential rotated for id:user (${credential_id}:${user_account}";<br /> exit 0;<br />}<br /><br /><br />run_rotate $@;</aws_iam_username></jenkins_credential_id></pre><pre><jenkins_credential_id><aws_iam_username><br></aws_iam_username></jenkins_credential_id></pre></div><p>One of the steps that happen within that script is a call to a groovy script. This groovy script handles the updating of the credential within Jenkins.</p><div class="gistLoad" id="gist-971fe3acdd9c42b09feb4bcacc365c25" data-file="set-credential.groovy" data-id="971fe3acdd9c42b09feb4bcacc365c25"><pre>#!/usr/bin/env groovy<br /><br />import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl<br />import jenkins.model.Jenkins<br /><br /><br />def updateCredential = { id, old_access_key, new_access_key, new_secret_key -&gt;<br /> println "Running updateCredential: (\"$id\", \"$old_access_key\", \"$new_access_key\", \"************************\")"<br /> def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(<br /> com.cloudbees.jenkins.plugins.awscredentials.BaseAmazonWebServicesCredentials.class,<br /> jenkins.model.Jenkins.instance<br /> )<br /><br /> def c = creds.findResult { it.id == id &amp;&amp; it.accessKey == ? it : null }<br /><br /> if ( c ) {<br /> println "found credential ${c.id} for access_key ${c.accessKey}"<br /> println c.class.toString()<br /> def credentials_store = jenkins.model.Jenkins.instance.getExtensionList(<br /> 'com.cloudbees.plugins.credentials.SystemCredentialsProvider'<br /> )[0].getStore()<br /> def tscope = c.scope as com.cloudbees.plugins.credentials.CredentialsScope<br /> def result = credentials_store.updateCredentials(<br /> com.cloudbees.plugins.credentials.domains.Domain.global(),<br /> c,<br /> new com.cloudbees.jenkins.plugins.awscredentials.AWSCredentialsImpl(tscope, c.id, new_access_key, new_secret_key, c.description, null, null)<br /> )<br /><br /> if (result) {<br /> println "password changed for id: ${id}"<br /> } else {<br /> println "failed to change password for id: ${id}"<br /> }<br /> } else {<br /> println "could not find credential for id: ${id}"<br /> }<br />}<br /><br /><br />if ( env['CI_ROTATE_CREDENTIAL_ID'] == null || env['CI_ROTATE_NEW_ACCESS_KEY_ID'] == null || env['CI_ROTATE_NEW_SECRET_KEY'] == null || env['CI_ROTATE_OLD_ACCESS_KEY_ID'] == null ||<br /><br /> env['CI_ROTATE_CREDENTIAL_ID'] == '' || env['CI_ROTATE_NEW_ACCESS_KEY_ID'] == '' || env['CI_ROTATE_NEW_SECRET_KEY'] == '' || env['CI_ROTATE_OLD_ACCESS_KEY_ID'] == ''<br />) {<br /> println "Missing value for 'CI_ROTATE_CREDENTIAL_ID', 'CI_ROTATE_NEW_ACCESS_KEY_ID', or 'CI_ROTATE_NEW_SECRET_KEY'"<br /> println "CI_ROTATE_CREDENTIAL_ID: ${env['CI_ROTATE_CREDENTIAL_ID']}"<br /> println "CI_ROTATE_OLD_ACCESS_KEY_ID: ${env['CI_ROTATE_OLD_ACCESS_KEY_ID']}"<br /> println "CI_ROTATE_NEW_ACCESS_KEY_ID: ${env['CI_ROTATE_NEW_ACCESS_KEY_ID']}"<br />} else {<br /> updateCredential("${env['CI_ROTATE_CREDENTIAL_ID']}", "${env['CI_ROTATE_OLD_ACCESS_KEY_ID']}", "${env['CI_ROTATE_NEW_ACCESS_KEY_ID']}", "${env['CI_ROTATE_NEW_SECRET_KEY']}")<br />}</pre></div><p>We used a <code>Jenkinsfile</code> to define the job that runs this, but I will leave that up to you…</p><p>When the jenkins job runs, it will invoke the <code>rotate.sh</code> script like this:</p><div class="gistLoad" id="gist-98d6a5d85ccd450370a947b6dd60076a" data-file="usage.sh" data-id="98d6a5d85ccd450370a947b6dd60076a"><pre>./rotate.sh -i "7e8f7b9e-0331-4266-bbd7-a5640326a0b0" -u "jenkins-deployment"</pre></div><p>Now the access key for jenkins will always be in compliance, and we will know that only jenkins is using the key. </p><p><br></p><p>This script assumes that the jenkins user has already assumed the role that is needed to perform the changes to the IAM user and that the credentials for that user are already set before it is called. Also, as of writing this, this script is not fully tested. This is the initial work I have done to perform this task. YMMV.</p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/0TQGUZvHV34" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/08/using-jenkins-to-rotate-aws-access-key.htmltag:blogger.com,1999:blog-3134018267377916851.post-91527021191591362522017-07-27T20:26:00.001-05:002017-07-27T20:26:24.915-05:00Configure NginX reverse proxy and OctoPrint<p>I have a raspberry pi on my network that is an NginX reverse proxy so I can have SSL termination and friendly names for some services on my network. </p><p>One of those services is OctoPrint for my PrusaI3 clone 3D printer. The host name for that raspberry pi is <code>octopi01.bit13.local</code> (<code>bit13.local</code> is my local domain) I want to be able to get to it in the browser by going to <code>prusai3.bit13.local</code>.</p><p>I have configured my DNS, also running on the same raspberry pi, to have an alias for <code>prusai3.bit13.local</code> to point to the nginx host (<code>dns01.bit13.local.</code>).</p><p>I then added an nginx config for the host so it will force SSL/TLS and proxy to the original host.</p><div class="gistLoad" id="gist-d4cd03e6c8533617cf23c112ff3bcfea" data-id="d4cd03e6c8533617cf23c112ff3bcfea" data-file="octoprint-nginx-proxy.config"><pre>server {<br /> # listen on port 80<br /> listen 80;<br /> server_name prusai3.bit13.local;<br /> # send anyone that comes here on port 80 -&gt; 443<br /> return 301 https://prusai3.bit13.local$request_uri;<br />}<br /><br />server {<br /> # listen on 443<br /> listen 443;<br /> server_name prusai3.bit13.local;<br /><br /> # enable ssl<br /> ssl on;<br /> # this is just a self-signed cert<br /> ssl_certificate /etc/nginx/ssl/server.crt;<br /> ssl_certificate_key /etc/nginx/ssl/server.key;<br /><br /> location / {<br /> # DNS address<br /> resolver 192.168.2.1;<br /> <br /> # set some proxy headers<br /> proxy_set_header Host $http_host;<br /> proxy_set_header X-Real-IP $remote_addr;<br /> proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br /><br /> # support WSS<br /> proxy_http_version 1.1;<br /> proxy_set_header Upgrade $http_upgrade;<br /> proxy_set_header Connection "upgrade";<br /> <br /> # pass on to the original host<br /> proxy_pass http://octopi01.bit13.local:80;<br /> }<br />}</pre><pre>Once I apply these changes, I can then access the OctoPrint website by visiting <code>https://prusai3.bit13.local</code> from my browser.</pre></div><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/nfFqJAYLEqY" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/07/configure-nginx-reverse-proxy-and.htmltag:blogger.com,1999:blog-3134018267377916851.post-84719057385192228562017-05-28T00:46:00.001-05:002017-05-29T09:28:56.372-05:00Access Windows Environment Variables from within Bash in WSL<p>I have been using my MacBook a lot now that it is my main computer at work. So much so that I found it necessary to invert my scroll wheel on my mouse on my windows desktop to behave like my MacBook. I've also been using <cod>bash a lot more since I can have similar experience between the 2 machines.</cod></p> <p>While in the process of writing the scripts to configure my bash environment on my Windows machine, I found the need to be able to access environment variables that are set in Windows. With WSL, the only environment variables that really come over to bash is <code>PATH</code>.</p> <p>I googled around for a bit, but didn't find any way to actually do this. Then I remembered that WSL has interop between Windows and WSL. This means that I can execute a Windows executable and redirect the output back to bash. Which means I should be able to execute <code>powershell.exe</code> to get the information I need.</p> <p>I first started with a test of just doing:</p><p><pre>$ echo $(powershell.exe -Command "gci ENV:")</pre> <p>And that gave me what I wanted back. Now there are some differences in the paths between WSL and Windows, so I knew I would also have to adjust for that.</p> <p>What I did was put a file called <code>~/.env.ps1</code> in my home path. </p><p> <div class="gistLoad" id="gist-2efbd85fd58753fc1a6aa90c3ff0f5a7" data-file=".env.ps1" data-id="2efbd85fd58753fc1a6aa90c3ff0f5a7"><pre>#!~/bin/powershell
<br /># Will return all the environment variables in KEY=VALUE format
<br />function Get-EnvironmentVariables {
<br /> return (Get-ChildItem ENV: | foreach { "WIN_$(Get-LinuxSafeValue -Value ($_.Name -replace '\(|\)','').ToUpper())=$(Convert-ToWSLPath -Path $_.Value)" })
<br />}
<br />
<br /># converts the C:\foo\bar path to the WSL counter part of /mnt/c/foo/bar
<br />function Convert-ToWSLPath {
<br /> param (
<br /> [Parameter(Mandatory=$true)]
<br /> $Path
<br /> )
<br /> (Get-LinuxSafeValue -Value (($Path -split ';' | foreach {
<br /> if ($_ -ne $null -and $_ -ne '' -and $_.Length -gt 0) {
<br /> (( (Fix-Path -Path $_) -replace '(^[A-Za-z])\:(.*)', '/mnt/$1$2') -replace '\\','/')
<br /> }
<br /> } ) -join ':'));
<br />}
<br />
<br />function Fix-Path {
<br /> param (
<br /> [Parameter(Mandatory=$true)]
<br /> $Path
<br /> )
<br /> if ( $Path -match '^[A-Z]\:' ) {
<br /> return $Path.Substring(0,1).ToLower()+$Path.Substring(1);
<br /> } else {
<br /> return $Path
<br /> }
<br />}
<br />
<br /># Ouputs a string of exports that can be evaluated
<br />function Import-EnvironmentVariables {
<br /> return (Get-EnvironmentVariables | foreach { "export $_;" }) | Out-String
<br />}
<br />
<br /># Just escapes special characters
<br />function Get-LinuxSafeValue {
<br /> param (
<br /> [Parameter(Mandatory=$true)]
<br /> $Value
<br /> )
<br /> process {
<br /> return $Value -replace "(\s|'|`"|\$|\#|&|!|~|``|\*|\?|\(|\)|\|)",'\$1';
<br /> }
<br />}</pre></div> <p>Now in my `.bashrc` I have the following:</p><p> <div class="gistLoad" id="gist-2efbd85fd58753fc1a6aa90c3ff0f5a7" data-file=".bashrc" data-id="2efbd85fd58753fc1a6aa90c3ff0f5a7"><pre>#!/usr/bin/env bash
<br />
<br />source ~/.wsl_helper.bash
<br />eval $(winenv)</pre></div> <p>If I run <code>env</code> now, I get output like the following:</p><p> <div class="gistLoad" id="gist-2efbd85fd58753fc1a6aa90c3ff0f5a7" data-file="example.output.txt" data-id="2efbd85fd58753fc1a6aa90c3ff0f5a7"><pre>WIN_ONEDRIVE=/mnt/d/users/rconr/onedrive
<br />PATH=~/bin:/foo:/usr/bin
<br />WIN_PATH=/mnt/c/windows:/mnt/c/windows/system32</pre></div> <p>Notice the environment variables that are prefixed with <i>WIN_</i>? These are environment variables directly from Windows. I can now add additional steps to my <code>.bashrc</code> using these variables.</p><p> <div class="gistLoad" id="gist-2efbd85fd58753fc1a6aa90c3ff0f5a7" data-file="example.call.bash" data-id="2efbd85fd58753fc1a6aa90c3ff0f5a7"><pre>ln -s "$WIN_ONEDRIVE" ~/OneDrive</pre></div> <hr> Additionally, I added a script to my <code>~/bin</code> folder that is in my path called <code>powershell</code>. This will allow me to make "native" style calls to powershell from within <code>bash</code> scripts. <div class="gistLoad" id="gist-2efbd85fd58753fc1a6aa90c3ff0f5a7" data-file="powershell" data-id="2efbd85fd58753fc1a6aa90c3ff0f5a7"><pre>
<br />#!/usr/bin/env bash
<br />
<br /># rename to `powershell`
<br /># chmod +x powershell
<br />
<br />. ~/.wsl_helper.bash
<br />
<br />PS_WORKING_DIR=$(lxssdir)
<br />if [ -f "$1" ] && "$1" ~= ".ps1$"; then
<br /> powershell.exe -NoLogo -ExecutionPolicy ByPass -Command "Set-Location '${PS_WORKING_DIR}'; Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Get-Content $1))) ${*:2}"
<br />elif [ -f "$1" ] && "$1" ~!= "\.ps1$"; then
<br /> powershell.exe -NoLogo -ExecutionPolicy ByPass -Command "Set-Location '${PS_WORKING_DIR}'; Invoke-Command -ScriptBlock ([ScriptBlock]::Create((Get-Content $1))) ${*:2}"
<br />else
<br /> powershell.exe -NoLogo -ExecutionPolicy ByPass ${*:1}
<br />fi
<br />unset PS_WORKING_DIR</pre></div> <p>In the <code>powershell</code> file, you will see a call to <code>source</code> a file called <code>.wsl_helper.bash</code>. This script has some helper functions that will do things like transform a path from a Windows style path to a linux WSL path, and do the opposite as well.</p> <p><div class="gistLoad" id="gist-2efbd85fd58753fc1a6aa90c3ff0f5a7" data-file=".wsl_helper.bash" data-id="2efbd85fd58753fc1a6aa90c3ff0f5a7"><pre>#!/usr/bin/env bash
<br /># This is the translated path to where the LXSS root directory is
<br />export LXSS_ROOT=/mnt/c/Users/$USER/AppData/Local/lxss
<br />
<br /># translate to linux path from windows path
<br />function windir() {
<br /> echo "$1" | sed -e 's|^\([a-z]\):\(.*\)|/mnt/\L\1\E\2|' -e 's|\\|/|g'
<br />}
<br />
<br /># translate the path back to windows path
<br />function wsldir() {
<br /> echo "$1" | sed -e 's|^/mnt/\([a-z]\)/\(.*\)|\U\1\:\\\E\2|' -e 's|/|\\|g'
<br />}
<br />
<br /># gets the lxss path from windows
<br />function lxssdir() {
<br /> if [ $# -eq 0 ]; then
<br /> if echo "$PWD" | grep "^/mnt/[a-zA-Z]/" > /dev/null 2>&1; then
<br /> echo "$PWD";
<br /> else
<br /> echo "$LXSS_ROOT$PWD";
<br /> fi
<br /> else
<br /> echo "$LXSS_ROOT$1";
<br /> fi
<br />}
<br />
<br />function printwinenv() {
<br /> _winenv --get
<br />}
<br />
<br /># this will load the output exports of the windows envrionment variables
<br />function winenv() {
<br /> _winenv --import
<br />}
<br />
<br />function _winenv() {
<br /> if [ $# -eq 0 ]; then
<br /> CMD_VERB="Get"
<br /> else
<br /> while test $# -gt 0; do
<br /> case "$1" in
<br /> -g|--get)
<br /> CMD_VERB="Get"
<br /> shift
<br /> ;;
<br /> -i|--import)
<br /> CMD_VERB="Import"
<br /> shift
<br /> ;;
<br /> *)
<br /> CMD_VERB="Get"
<br /> break
<br /> ;;
<br /> esac
<br /> done
<br /> fi
<br /> CMD_DIR=$(wsldir "$LXSS_ROOT$HOME/\.env.ps1")
<br /> echo $(powershell.exe -Command "Import-Module -Name $CMD_DIR; $CMD_VERB-EnvironmentVariables") | sed -e 's|\r|\n|g' -e 's|^[\s\t]*||g';
<br />}</pre></div><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/fYS5fs7urBc" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/05/access-windows-environment-variables.htmltag:blogger.com,1999:blog-3134018267377916851.post-34809622754574172912017-05-17T09:33:00.005-05:002017-05-26T22:45:50.533-05:00Jenkins + NPM Install + Git<p>I have been working on setting up Jenkins Pipelines for some projects and had an issue that I think others have had, but I could not find a clear answer on the way to handle it.</p> <p>We have some NPM Packages that are pulled from a private git repo, and all of the accounts have MFA enabled, including the CI user account. This means that SSH authentication is mandatory for CI user.</p> <p>If there is only one host that you need to ssh auth with jenkins, or you use the exact same ssh key for all hosts, then you can just put the private key on your Jenkins server at <code>~/.ssh/id_rsa</code>. If you need to specify a key dependant upon the host, which is the situation I was in, it was not working to pull the package.</p> <p>The solution for this that I found was to use the <code>~/.ssh/config</code>. In there you specify the hosts, the user, and what identity file to use. It can look something like this:</p><pre><code>Host github.com<br /> User git<br /> IdentityFile ~/.ssh/github.key<br /><br />Host bitbucket.org<br /> User git<br /> IdentityFile ~/.ssh/bitbucket.key<br /><br />Host tfs.myonprem-domain.com<br /> User my-ci-user<br /> IdentityFile ~/.ssh/onprem-tfs.key<br /></code></pre><p dir="ltr">So now, when running <code>npm install</code>, ssh will know what identity file to use.</p><p dir="ltr">Bonus tip: Not everyone uses ssh, so in the package.json, it may not be configured to use ssh. You can put options in the global .gitconfig on the Jenkins server that will <code>redirect</code> the <code>https</code> protocol requests to <code>ssh</code>:</p><pre><code>[url "ssh://git@github.com/"]<br /> insteadOf = "https://github.com/"<br />[url "ssh://git@bitbucket.org/"]<br /> insteadOf = "https://bitbucket.org/"<br />[url "ssh://tfs.myonprem-domain.com:22/"]<br /> instadOf = "https://tfs.myonprem-domain.com/<br /></code></pre><p>&nbsp;</p><p>So with that, when git detects an <code>https</code> request, it will switch to use <code>ssh</code>.</p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/9jkd7UwZsIY" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/05/jenkins-npm-install-git.htmltag:blogger.com,1999:blog-3134018267377916851.post-53069540730773959412017-01-10T23:23:00.001-06:002017-01-10T23:23:21.117-06:00Type 2 Diabetes Diagnosis<p>I won't go through all the details, but on December 26th, 2016, I was diagnosed with Type 2 Diabetes. As you probably expect, it means a drastic life-style change for me. I have to give insulin injections, and monitor blood glucose, as well as monitor the amount of carbohydrates that I consume. To help me track that information I found an application on Google Play called <a href="http://www.diabetes-m.com/" target="_blank">Diabetes:M</a> (<a href="https://play.google.com/store/apps/details?id=com.mydiabetes&amp;hl=en" target="_blank">Play Store</a>). The application is free, but ad supported. You can purchase the ad-free license for ~$9 or so, which I purchased as soon as I was sure it was the application that I wanted to use.</p> <p><img title="2017-01-11 04.04.33" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: left; padding-top: 0px; padding-left: 0px; margin: 0px 5px 0px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="2017-01-11 04.04.33" src="https://lh3.googleusercontent.com/-9lYuhBAm-pI/WHXBL_2q2kI/AAAAAAAA170/6v-_nrs85Z8/2017-01-11%25252004.04.33%25255B5%25255D.png?imgmax=800" width="270" align="left" height="480"></p> <p>This allows me to track everything that I need to be successful in controlling my diabetes. This includes information like my meals, my medications, blood pressure, insulin injections, etc. One of my favorite features of Diabetes:M is the ability for the application to sync the SQLite database, that contains all of the logged data, to a cloud storage provider like Dropbox. This, as a developer, got me thinking how I could leverage that so that I can provide my information to my wife so she is aware of what my current blood glucose level is. </p> <p>To start this project I created an Express node application and put a copy of the SQLite database in the directory structure of the application. I then went to work on creating some SQL queries to pull information from the Diabetes:M database. To start, I queried my most recent glucose level. </p> <p><a href="https://directorymonitor.com/" target="_blank"><img title="dm" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="dm" src="https://lh3.googleusercontent.com/-Iu1GlnudzLE/WHXBNOkgOtI/AAAAAAAA174/IeDqUD2UpTU/dm%25255B4%25255D.png?imgmax=800" width="345" height="93"></a></p> <p>One of the important steps to make the data current was for me to find a way to detect when a new version of the database was saved to my Dropbox. To do this, I found an application (for windows) called <a href="https://directorymonitor.com/" target="_blank">Directory Monitor</a>. There is a free version of the application, which is what I am currently using, as the free version fits all my needs. I configured the application to run a shell script when the SQLite database file is modified on my system, which happens every time I log information in the Android application. </p> <p><a href="https://lh3.googleusercontent.com/-6jl-VGkg5Qs/WHXBONciDfI/AAAAAAAA178/hBly8vDPTfI/s1600-h/dm-info%25255B8%25255D.png"><img title="dm-info" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="dm-info" src="https://lh3.googleusercontent.com/-N_V_rHTreAQ/WHXBPDu5Q0I/AAAAAAAA18A/P-7z3RDWIcU/dm-info_thumb%25255B2%25255D.png?imgmax=800" width="927" height="444"></a></p> <p>The shell script goes and extracts the <code>dbz</code> file, which is just a gzipped (or some other compression) SQLite file. It takes that extracted database file and puts it in the <code>/data</code> directory in the express application. It then does a <code>git commit</code> and <code>git push</code> of the git repository. This commit triggers a deploy in Azure. Within seconds of me taking my glucose level, the database is updated on Azure, so when my wife wants to check to see how I am doing, she can see the data in near real-time. </p> <p>So when she opens the site on her phone she would see something like the following:</p> <p>&nbsp;</p> <p><img title="current-level" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="current-level" src="https://lh3.googleusercontent.com/-yYXLi5T22f4/WHXBQCinvFI/AAAAAAAA18E/L_eFWxFHJ2Y/current-level%25255B4%25255D.png?imgmax=800" width="741" height="488"></p> <p>&nbsp;</p> <p>She can even see a history of all the events that I have entered into Diabetes:M</p> <p>&nbsp;</p> <p><img title="log" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="log" src="https://lh3.googleusercontent.com/-Q9zokaxWzd8/WHXBRFTNyJI/AAAAAAAA18I/WScE9mR4rhM/log%25255B4%25255D.png?imgmax=800" width="589" height="954"></p> <p>&nbsp;</p> <p>And, because I could, I added a dashboard with some charts</p> <p><img title="db1" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="db1" src="https://lh3.googleusercontent.com/-5k3nh-btIcY/WHXBSNzGPVI/AAAAAAAA18M/HPJnkZpEHjc/db1%25255B4%25255D.png?imgmax=800" width="1120" height="700"></p> <p>This is just the beginning of what I have done. I have other things I want to add so my wife, and family, can support me in managing this so I can live a healthier life. Right now, the code for this is not publicly available because it is still very tied to my information and configuration. I will work towards getting this in a place that I can share the code so others can use this. </p> <p>&nbsp;</p> <p>Some other ideas that I think will be fun, and useful:</p> <ul> <li>Alexa Skill – Wife will be able to ask Alexa what my glucose is, and will respond with something like "about 32 minutes ago, Ryan's glucose was 132" <li>iOS/Android 'support' app to be able to receive push notifications when there is a reason for concern, like high, or low glucose levels. <li>More detailed reports and the ability to change the timespan of the data. <li>Support for other systems to monitor the database changes. I have multiple Raspberry PI's that are always running, one of them could monitor the database for changes in Dropbox and the rPi could sync the database. <li><a href="http://www.nightscout.info/" target="_blank">Nightscout (CGM in the Cloud)</a> – Integration / Support for Nightscout and all the work that the community has already done here.</li></ul><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/AFnrvk4UhOk" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2017/01/type-2-diabetes-diagnosis.htmltag:blogger.com,1999:blog-3134018267377916851.post-43588122644829213162016-06-12T12:05:00.000-05:002016-06-12T12:07:39.911-05:00Installing Git on a Jenkins Windows Slave<p>Recently working on getting a Jenkins Windows build slave up and running, I noticed that the builds would hang, and timeout. The build would get stuck during the clone of the repository. Furthermore, if the build tried to run again, it would error out while trying to clean up the workspace.</p> <p>We are using Chef and the Jenkins Cookbook to set up the Windows slave. Git for Windows is installed via Chocolatey. After a couple days of trial and error, here is what I found, and how I fixed it.</p> <p>&nbsp;</p> <p>The latest versions of Git for Windows installer, we are installing 2.8.4, installs the Credential Manager by default. I have been unable to find a way to disable this option during the install, if you know a way, I would love to hear the solution to that. </p> <p>(Here is a screenshot I found of the option, though it is from the 2.7.4 installer)<br><img src="http://i.imgur.com/03xWzl7.jpg"></p> <p><br>The credential manager stores your git user/password for your repositories, so you don’t have to enter them every time. Well, we are using Jenkins, and Jenkins does not get mad about having to enter the credentials every time. Also, the credential manager pops up this dialog when you try to clone a repository that requires authentication<br><img src="http://i.imgur.com/weUeCgD.png"></p> <p>&nbsp;</p> <p>When this pops up, the Jenkins build executor is stuck because it does not know how to enter input into this dialog. </p> <p>&nbsp;</p> <p>We need to tell git that we do no want to use this credential helper, since we have no way to prevent it from being installed. To do this, with chef, I created a file in <code>files/default</code> called <code>base.gitconfig</code>. <br><pre>[core]
<br /> autocrlf = true
<br /> askpass = true
<br /></pre>
<p>Next, in the recipe, I put the file on the system in <code>c:\program files\git\mingw64\etc\</code> named <code>gitconfig</code>.<br><pre>cookbook_file "C:/Program Files/Git/mingw64/etc/gitconfig" do
<br /> source 'base.gitconfig'
<br /> owner node['jenkins']['os']['windows']['user']
<br /> group node['jenkins']['os']['windows']['group']
<br />end
<br /></pre>
Now when the Windows slave attempts to do a <code>git clone</code>, it no longer will use the credential manager and pop up the dialog, and everything works flawlessly.
<img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/xDh2AFpLXcA" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2016/06/installing-git-on-jenkins-windows-slave.htmltag:blogger.com,1999:blog-3134018267377916851.post-34181483550652812642016-04-14T21:02:00.001-05:002016-04-14T21:02:02.946-05:00Change Colors in PowerShell Console<p>I have my powershell console window have a black background and slightly transparent. The default colors here work for most things, except errors. Errors were hard to read, especially when projecting to a secondary monitor. I found on the web that you can change the foreground color for powershell errors by setting <em>$host.privatedata.ErrorForegroundColor. </em>But the problem with these solutions that I found was that they just said “from a PS prompt, set the color”. I wanted something a little more permanent. </p> <p>What I came up with was to set the values that I want in my PowerShell profile file that is located at <em>[Environment]::GetFolderPath("MyDocuments")/WindowsPowerShell/Microsoft.PowerShell_profile.ps1</em>.<br><br>Here is what I put in mine:</p><pre>$host.privatedata.ErrorForegroundColor = 'Magenta';
<br />$host.privatedata.ErrorBackgroundColor = 'Black';
<br />$host.privatedata.WarningForegroundColor = 'Yellow';
<br />$host.privatedata.WarningBackgroundColor = 'Black';
<br />$host.privatedata.DebugForegroundColor = 'DarkGray';
<br />$host.privatedata.DebugBackgroundColor = 'Black';
<br />$host.privatedata.VerboseForegroundColor = 'White';
<br />$host.privatedata.VerboseBackgroundColor = 'Black';
<br />$host.privatedata.ProgressForegroundColor = 'DarkBlue';
<br />$host.privatedata.ProgressBackgroundColor = 'DarkCyan';</pre>
<p>&nbsp;</p>
<p><a href="https://lh3.googleusercontent.com/-PSYxa_GbtK4/VxBLl51AuOI/AAAAAAAAwwk/_IIsULX5GaU/s1600-h/image%25255B15%25255D.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://lh3.googleusercontent.com/-bPPVghAc2kY/VxBLmrfEn-I/AAAAAAAAwws/7ebCOrq8o9M/image_thumb%25255B9%25255D.png?imgmax=800" width="888" height="167"></a></p>
<p>Now my errors are readable. </p>
<p>To see what values you can set them to just execute: <em>[system.consolecolor]::GetNames("consolecolor").</em></p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/Ic5BEsTd9u0" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2016/04/change-colors-in-powershell-console.htmltag:blogger.com,1999:blog-3134018267377916851.post-67944776535833605812016-04-12T22:30:00.001-05:002016-04-12T22:30:38.767-05:00Running kitchen converge on Windows 2012r2 image on OpenStack<p>I have been working with my team to get Windows 2012r2 image chef cookbook to converge in an OpenStack environment. We have run in to a lot of issues, and I am confident that some of those were because of my lack of experience with chef/windows. One of the big issues that we encountered was kitchen unable to WINRM in to the box. It would just keep throwing [winrm] HTTPClient::ConnectionTimeoutException. We noticed that if someone manually logs on to the machine, and they are presented with the <em>User must change password </em>for the account. After they enter the password, they are prompted with <em>Choose the type of network</em> for the machine.</p> <p>This had me thinking. If the user account is set to require the password to be changed, Windows will not allow anyone to remotely logon to the machine with that account. So I tested this theory. I spun up the Windows2012r2 image in OpenStack and attempted to connect with <em>Enter-PSSession.</em> I was getting an error that the connection could not be established. Specifically:</p><pre>WinRM cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, <br>and that a firewall exception for the WinRM service is enabled and allows access from this computer. By default, the WinRM firewall <br>exception for public profiles limits access to remote computers within the same local subnet.</pre>
<p>This lead me to determine that because the network type has not been selected, the second thing the user is presented with after manually logging in, WinRM is not letting the connection happen. I then figured that because this machine has CloudInit, there has to be a way to set the network type to be private. If this machine was added to the Domain, this would not be an issue, but this is a ephemeral machine just to test the changes to the cookbook and verify it works so it is not on the Domain. After some research I came across <em><a href="https://blogs.msdn.microsoft.com/powershell/2009/04/02/setting-network-location-to-private/" target="_blank">Setting Network Location to Private</a> </em>blog post by Vladimir Averkin. This post has a block of PowerShell that enumerates all networks, post-Vista machines that are not joined to a Domain, and sets them to Private. </p><pre># Skip network location setting for pre-Vista operating systems<br>if([environment]::OSVersion.version.Major -lt 6) { return }
<br /># Skip network location setting if local machine is joined to a domain.<br>if(1,3,4,5 -contains (Get-WmiObject win32_computersystem).DomainRole) { return }
<br /># Get network connections<br>$networkListManager = [Activator]::CreateInstance([Type]::GetTypeFromCLSID([Guid]"{DCB00C01-570F-4A9B-8D69-199FDBA5723B}"))<br>$connections = $networkListManager.GetNetworkConnections()
<br /># Set network location to Private for all networks<br>$connections | % {$_.GetNetwork().SetCategory(1)}</pre>
<p>Perfect! </p>
<p>I launched a new instance on OpenStack, this time in the Post-Creation I added the script above, making sure to specify <code>#ps1_sysnative</code> at the top to let cloudinit know that we are running some PowerShell. Then waited…</p>
<p>Fired up PowerShell and ran <code>Enter-PSSession</code>... Failed, <code>Access is denied</code>. This has to be the issue with the user being prompted to change the password. Went back into OpenStack, launched another instance. This time at the end of the above script, I added:</p><pre>net user KitchenCIUser KitchenCIUserPassword</pre>
<p>Then I waited again… I started running <code>Test-WSMan</code> until it came back successful. Then I ran <code>Enter-PSSession</code> one more time. SUCCESS! I was now connected to the machine via PSRemoting. </p>
<p>Now one last hurdle, how do I specify the CloudInit script with the .kitchen.yml. Well, as it turns out, the <a href="https://github.com/test-kitchen/kitchen-openstack" target="_blank">kitchen-openstack</a> driver supports this. </p><pre>driver_config:
<br /> user_data: cloud-init.txt</pre>
<p>Then just have to create the cloud-init.txt file in the same path as the .kitchen.yml. I saved the changes, committed to repository and watched as jenkins kicked off the build. Oh, and I waited…</p>
<p>Success! Kitchen was able to use WinRM and converge the box, now on to the next problem that I am sure we will encounter.</p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/xrT8ks7CnnE" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2016/04/running-kitchen-converge-on-windows.htmltag:blogger.com,1999:blog-3134018267377916851.post-67715384688024038982016-04-02T23:05:00.001-05:002016-04-02T23:05:38.632-05:00Droid Explorer working on Non-Rooted Device<p>I have started doing some work on removing the dependency on rooted device, specifically, busybox. This is no where near complete, and is buggy, at best. As you can see from the screenshot, you can see most of the folders, but if you try to go in to some of them, you will not see any files. This is my Nexus 6P, which is not rooted, connected to Droid Explorer. I do not have busybox or anything else special installed.</p> <p><img title="no-root" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="no-root" src="https://lh3.googleusercontent.com/-M8aepqkcBqs/VwCWkQdYD1I/AAAAAAAAwsQ/FlL9uwHXIwk/no-root%25255B9%25255D.png?imgmax=800" width="767" height="458"></p> <p>It doesn’t look like much, but this is a big change to the code base. I have been removing code from the Core.CommandRunner, the code that handles 99% of the communication between your computer and your device. Instead it is making use of <a href="https://github.com/camalot/madb" target="_blank">madb</a>, which minimizes the need for adb, and only uses it to act as the adb server. While the old code used adb as the server, but also depended on the adb client to do the communication to the device. Madb is the client, and it does all the communication to the adb server. </p> <p>&nbsp;</p> <p>No binaries available yet, as I stated, this is very early. The code will be available on the <a href="https://github.com/camalot/droidexplorer" target="_blank">Droid Explorer Github</a> soon.</p><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/MxtFsZLZsjI" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2016/04/droid-explorer-working-on-non-rooted.htmltag:blogger.com,1999:blog-3134018267377916851.post-44352220436983385642016-02-27T19:45:00.001-06:002016-02-27T19:45:37.104-06:00VirtualBox VT-X/AMD-V Hardware Acceleration Error<p>I was getting this error about VT-X/AMD-V Hardware Acceleration not being available on my system and was very confused because I had enabled it in the BIOS and still got the error. Today I decided to do a little research on it. I had remembered in the past reading about Hyper-V causing issues with other virtualization software. So I did some checking on my system, because I know that Hyper-V is enabled/installed when you install the Android Emulator with Visual Studio. I checked if the Hyper-V feature was enabled, and sure enough, it was. I disabled Hyper-V and rebooted my system. Fired up VirualBox and loaded up the VM. Success.</p> <p>To disable Hyper-V </p> <ul> <li>Go to Control Panel –&gt; Programs and Features</li> <li>Click Turn Windows features on or off</li> <li>Uncheck Hyper-V<br><img src="http://i.imgur.com/suEwqrJ.png"></li> <li>Click OK</li> <li>Restart</li></ul><img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/g16zW-vvWvI" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2016/02/virtualbox-vt-xamd-v-hardware.htmltag:blogger.com,1999:blog-3134018267377916851.post-65602841628096416512015-11-20T11:46:00.001-06:002015-11-20T11:51:25.447-06:00How to Run Hubot on Azure Websites<p>I wanted to get <a href="http://hubot.github.com/" target="_blank">Hubot</a> working in our slack channels at work and I thought I would share the process I took to get it working and running on Azure Websites.</p> <p>&nbsp;</p> <p>First, make sure you have nodejs installed</p> <blockquote> <p>&gt; choco install nodejs.install -y</p></blockquote> <p>Make sure npm is up to date</p> <blockquote> <p>&gt; npm install -g npm</p></blockquote> <p>At this point, I also installed coffee-script because the first time I tried running it, I got errors about coffee script</p> <blockquote> <p>&gt; npm install -g coffee-script</p></blockquote> <p>Install the azure-cli node modules to generate the deploy file that will be used</p> <blockquote> <p>&gt; npm install –g azure-cli</p></blockquote> <p>Next we install the yo hubot generator</p> <blockquote> <p>&gt; npm install –g yo generator-hubot</p></blockquote> <p>create a directory for your hubot. I have found that you should not initially name it “hubot”, as npm will fail with some of the package installs (specifically, the hubot package). Then lets start the hubot configuration</p> <blockquote> <p>&gt; mkdir myhubot</p> <p>&gt; cd myhubot</p> <p>&gt; yo hubot</p></blockquote> <p>Now you will answer the question it asks to configure hubot. Again, I wouldn’t name it “hubot” when it asks. I will show you where to change it later so it will respond to hubot.</p> <p><img title="yohubot" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="yohubot" src="http://lh3.googleusercontent.com/-GPpoc4dbJxQ/Vk9cf5z363I/AAAAAAAAv0g/D9dXjIbRn8s/yohubot%25255B5%25255D.png?imgmax=800" width="822" height="589"> </p> <p>Mine showed some error messages while running this, but still functioned correctly.</p> <p>Now lets install the slack adapter</p> <blockquote> <p>&gt; npm install hubot-slack --save</p></blockquote> <p>next we need to create the azure deploy command.</p> <blockquote> <p>&gt; azure site deploymentscript –node</p></blockquote> <p>lets now make a couple tweaks to the hubot files:</p> <p>first, open up external-scripts.json and remove</p> <blockquote> <p>"hubot-heroku-keepalive",</p> <p>"hubot-redis-brain"</p></blockquote> <p>if you want to use azure blob for the brain we can set that up now. while still in the external-scripts.json, add the line:</p> <blockquote> <p>"hubot-azure-scripts/brain/storage-blob-brain"</p></blockquote> <p>Save the file and go back to the command prompt:</p> <blockquote> <p>&gt; npm install hubot-azure-scripts --save</p></blockquote> <p>You can go to <a title="https://github.com/hubot-scripts" href="https://github.com/hubot-scripts">https://github.com/hubot-scripts</a> and find some other script that you may want to add</p> <p>first install from npm:</p> <blockquote> <p>&gt; npm install [scriptname] --save</p></blockquote> <p>Then add it to the external-scripts.json</p> <p>&nbsp;</p> <p>next we have to modify the deploy.cmd slightly. There is an issue with azure where it doesn’t like that hubot doesn’t have a file extension. So we copy the hubot file and add the coffee extension.</p> <p>After <strong>:: 3. Install npm packages</strong> block add the following:</p> <blockquote> <p>:: 4. Create Hubot file with a coffee extension<br>copy /Y "%DEPLOYMENT_TARGET%\node_modules\hubot\bin\hubot" "%DEPLOYMENT_TARGET%\node_modules\hubot\bin\hubot.coffee"</p></blockquote> <p>Since we created the hubot.coffee, we need to configure the server to use that:</p> <blockquote> <p>&gt; echo require('coffee-script/register'); &gt; server.js &amp; echo module.exports = require('hubot/bin/hubot.coffee'); &gt;&gt; server.js</p></blockquote> <p>you can either copy this and run in command prompt, or create a file called <strong>server.js</strong> and add the following:</p> <blockquote> <p>require("coffee-script/register’);</p> <p>module.exports = require("hubot/bin/hubot.coffee");</p></blockquote> <p>Here is when we can change the name of the hubot back to acutally be hubot.</p> <blockquote> <p>Open ./myhubot/bin/hubot.cmd in notepad and change “myhubot” to “hubot”.</p> <p>You may also want to edit the readme.md and replace “myhubot” with “hubot” as well.</p></blockquote> <p>&nbsp;</p> <p>At this point you should make your github repo for your hubot and then commit your changes to github. (<strong>Use your github and your own repo</strong>)</p> <blockquote> <p>&gt; git init</p> <p>&gt; git add .</p> <p>&gt; git commit -m "initial hubot commit"</p> <p>&gt; git remote add origin https://github.com/[your-user-name]/[your-hubot-origin]</p> <p>&gt; git push -u origin master</p></blockquote> <p>&nbsp;</p> <p>Azure setup</p> <p>Login to your Azure portal and create a new Web App: (The name has to be unique)</p> <p><img title="webapp1" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="webapp1" src="http://lh3.googleusercontent.com/-q0EKB_xaL_0/Vk9cgv7IALI/AAAAAAAAv0o/kuOOTvjyqGI/webapp1%25255B5%25255D.png?imgmax=800" width="546" height="525"> </p> <p>&nbsp;</p> <p>If you are using Azure blob for the brain, create the storage account as well.</p> <p><img title="blob1" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="blob1" src="http://lh3.googleusercontent.com/--LQfNKiPmHU/Vk9chObFPJI/AAAAAAAAv0s/9-LWQdKD6WE/blob1%25255B5%25255D.png?imgmax=800" width="563" height="571"> </p> <p>&nbsp;</p> <p>After the storage is created, we will need the Storage Account Name, and Primary Access Key. Copy these for use a little later:</p> <p><img title="keys" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="keys" src="http://lh3.googleusercontent.com/-rdOOWftjkn0/Vk9ciFHnakI/AAAAAAAAv04/gAy9QEFEP8Y/keys%25255B5%25255D.png?imgmax=800" width="1222" height="515"> </p> <p></p> <p>Go in to the settings for your newly created web app and click on “Continuous Deployment”. We are going to configure Azure Web Site to pull directly from the Github repo for the hubot configuration.</p> <p>Choose Github as the provider and then authorize Azure to connect. Then choose your Hubot</p> <p><img title="cd" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="cd" src="http://lh3.googleusercontent.com/-rQxGBWagiYU/Vk9ci3T6t_I/AAAAAAAAv08/8-Eh2XoVkCk/cd%25255B5%25255D.png?imgmax=800" width="932" height="525"> </p> <p>&nbsp;</p> <p>Finally we have to set up the environment variables that Hubot needs to function:</p> <p><img title="properties" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="properties" src="http://lh3.googleusercontent.com/-dA9p2XYRmf8/Vk9cjqiFCkI/AAAAAAAAv1A/03rnzYL8Oys/properties%25255B5%25255D.png?imgmax=800" width="895" height="489"> </p> <p>You will create the following keys:</p> <p>HUBOT_SLACK_TOKEN:&nbsp; Your Slack Token for Hubot when you enable the integration</p> <p>HUBOT_SLACK_TEAM:&nbsp; <strong>myslack</strong> (from https://myslack.slack.com)</p> <p>HUBOT_SLACK_BOTNAME: The name of your hubot</p> <p>HUBOT_ADAPTER: <strong>slack</strong></p> <p>HUBOT_BRAIN_AZURE_STORAGE_ACCOUNT: The storage account name we saved from above</p> <p>HUBOT_BRAIN_AZURE_STORAGE_ACCESS_KEY: The storage access token we saved from above</p> <p>&nbsp;</p> <p>Go to your webapp to test that your hubot is working.</p> <p>http://myhubot1.azurewebsites.net/hubot/help</p> <p><img title="help" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="help" src="http://lh3.googleusercontent.com/-lSIYVANASN4/Vk9dnNMA8iI/AAAAAAAAv1I/LPnVQvtcI7c/help%25255B5%25255D.png?imgmax=800" width="925" height="553"> </p> <p>Sometimes it doesn’t respond the first time, but if it continues to fail when requesting, there there may be an issue. Try running locally (you will have to remove the azure-blob when running local) and see if everything is working.</p> <p>That’s about it. Enjoy!</p> <img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/gBYIQFMtZJU" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2015/11/how-to-run-hubot-on-azure-websites.htmltag:blogger.com,1999:blog-3134018267377916851.post-37224637908454808912015-11-06T23:28:00.001-06:002015-11-06T23:32:47.814-06:00Persistent Command History in PowerShell<p>If you are familiar with .bash_history or just wish you had a history of your powershell commands persist between sessions here is a handy little script. It will persist command history when you exit a powershell session to a file in %USERPROFILE% called .ps_history. </p> <p>Copy the code below and save to a file called “Microsoft.PowerShell_profile.ps1” and save it in “%USERPROFILE%\Documents\WindowsPowerShell”</p><pre>$HistoryFilePath = Join-Path -Path ([Environment]::GetFolderPath('UserProfile')) -ChildPath ".ps_history"; <br />function Persist-History {<br /> Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {<br /> Get-History | Select-Object -Unique | Export-Clixml -Path $HistoryFilePath;<br /> } | Out-Null;<br /> if (Test-path -Path $HistoryFilePath) { <br /> Import-Clixml -Path $HistoryFilePath | Add-History;<br /> }<br /> Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward;<br /> Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward;<br />} <br />Persist-History;</pre><pre>&nbsp;</pre><br /><p>Disclaimer: I have only tested this with PowerShell 5</p><br /><p>If it doesn’t work, you could probably make it work by installing the PackageManagement modules for PowerShell and install the “PSReadline” module.</p> <img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/78JtwCa6F4Q" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2015/11/persistent-command-history-in-powershell.htmltag:blogger.com,1999:blog-3134018267377916851.post-66768499532077135152015-10-31T21:31:00.001-05:002015-10-31T21:33:30.385-05:00Microsoft Channel9 Addon for Kodi<div style="float: right; margin-left: 10px"><img style="border: 0px !important;" border="0" src="https://raw.githubusercontent.com/camalot/plugin.video.microsoft.channel9/master/icon.png"> </div> <p>To add to the my list of hobby projects, I created a fork of an addon for Kodi that provides browsing of videos on Microsoft Channel9. This was my first attempt at a Kodi addon, and really my first time working with writing python. I started out by just reading over the original code and tweaking it a bit because some things were not working. Then I started following the pattern that existed and implementing new features, like being able to browse Channel9 authors, and Channel9 events.</p> <p>Once I got these features in place, I started doing some real refactoring of the original code base. There was a ton of code that was duplicated in multiple places. I spent a few hours of the day really rewriting the entire addon. As the project stands right now, it is basically a complete rewrite of the original addon.</p> <p>The project is on <a href="https://github.com/camalot/plugin.video.microsoft.channel9" target="_blank">Github</a> and building on AppVeyor.</p> <h3><a href="https://github.com/camalot/plugin.video.microsoft.channel9/releases" target="_blank">Download the latest Release</a></h3> <p><img style="border: 0px !important" border="0" src="http://i.imgur.com/fQuA6Bvl.jpg"></p> <p><img style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px" border="0" src="http://i.imgur.com/ZuLpUFQl.jpg"></p> <p><img style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px" border="0" src="http://i.imgur.com/xWzuSh5l.jpg"></p> <img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/AoCR_7bLFaw" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2015/10/microsoft-channel9-addon-for-kodi.htmltag:blogger.com,1999:blog-3134018267377916851.post-53306097360275535112015-10-04T21:50:00.001-05:002015-10-04T21:50:00.947-05:00Simplify standing up an IE Virtual Machine with PSIEVM<p>Over the weekend I was bored and decided to start a project to automate the process of standing up an IE VM with powershell. I would like to introduce <a href="https://github.com/camalot/psievm" target="_blank">PSIEVM</a>. A powershell module that allows you to stand up an IE VM in VirtualBox (other hosts coming in the future) in one line.</p> <blockquote> <p>PS&gt; Get-IEVM –OS Win7 –IEVersion 9</p></blockquote> <p>That is the simplest form of the command.</p> <h3>Installation</h3> <p>To install, download from the <a href="https://github.com/camalot/psievm/releases" target="_blank">github project releases page</a> and extract to <strong>$env:USERPROFILE\WindowsPowerShell\Modules</strong>. Then to use it just import it</p> <blockquote> <p>PS&gt; Import-Module psievm<br>PS&gt; Get-IEVM –OS Win7 –IEVersion 10 –AlternateVMLocation "G:\VMs\" –VMRootPath "G:\VMs\"</p></blockquote> <p>&nbsp;</p> <p>The parameters for Get-IEVM:</p> <table cellspacing="0" cellpadding="2" width="100%" border="1"> <tbody> <tr> <th valign="top" width="40%"> <p align="center"><strong>Name</strong></p></td> <th valign="top" width="20%"> <p align="center"><strong>Required</strong></p></td> <th valign="top" width="20%"> <p align="center"><strong>Type</strong></p></td> <th valign="top" width="20%"> <p align="center"><strong>Default</strong></p></td></tr> <tr> <td valign="top"> <p align="left">OS</p></td> <td valign="top"> <p align="center">YES</p></td> <td valign="top"> <p align="center">String</p></td> <td valign="top"> <p>[Empty]</p></td></tr> <tr> <td colspan="4"> <p>The OS to use:</p> <ul> <li>WinXP, WindowsXP, Windows XP, XP <li>WinVista, WindowsVista, Windows Vista, Vista <li>Win7, Windows7, Windows 7, 7 <li>Win8, Windows8, Windows 8, 8 <li>Win8.1, Windows8.1, Windows 8.1, 8.1 <li>Win10, Windows10, Windows 10, 10<br></li></ul></td></tr> <tr> <td valign="top"> <p align="left">IEVersion</p></td> <td valign="top"> <p align="center">YES</p></td> <td valign="top"> <p align="center">String</p></td> <td valign="top"> <p align="left">[Empty]</p></td></tr> <tr> <td colspan="4"> <p>The version of IE. This is dependant on the version of Windows (See the table below).</p> <ul> <li>6 <li>7 <li>8 <li>9 <li>10 <li>11 <li>Edge</li></ul></td></tr> <tr> <td valign="top"> <p align="left">Shares</p></td> <td valign="top"> <p align="center">NO</p></td> <td valign="top"> <p align="center">String[]</p></td> <td valign="top"> <p align="left">[Empty]</p></td></tr> <tr> <td colspan="4">These are the paths to add as a share in the VM through the VMHost. May not be fully supported in all VMHosts.</td></tr> <tr> <td valign="top"> <p align="left">AlternateVMLocation</p></td> <td valign="top"> <p align="center">NO</p></td> <td valign="top"> <p align="center">String</p></td> <td valign="top"> <p align="left">[Empty]</p></td></tr> <tr> <td colspan="4"> <p>By default the VM images will be downloaded from Microsoft, but if you have the images downloaded already, you can use this to tell the script where to get them. This can be a URL, Network Share, or a local path. The images need to follow the following pattern:</p> <p>IE: IE&lt;IEVersion&gt;.Win&lt;OSVersion&gt;.For.Windows.&lt;VMHost&gt;.zip<br>Edge: Microsoft%20Edge.Win10.For.Windows.&lt;VMHost&gt;.zip</p></td></tr> <tr> <td valign="top"> <p align="left">VMHost</p></td> <td valign="top"> <p align="center">NO</p></td> <td valign="top"> <p align="center">String</p></td> <td valign="top"> <p align="left">VirtualBox</p></td></tr> <tr> <td colspan="4"> <p>The VM Host to use. Only VirtualBox is currently supported but here is the full list that will be supported (and maybe others).</p> <ul> <li>VirtualBox <li>HyperV <li>Vagrent <li>VPC <li>VMWare</li></ul></td></tr> <tr> <td valign="top"> <p align="left">IgnoreInvalidMD5</p></td> <td valign="top"> <p align="center">NO</p></td> <td valign="top"> <p align="center">Boolean</p></td> <td valign="top"> <p align="left">$False</p></td></tr> <tr> <td colspan="4">After the zip image is downloaded, the file MD5 hash is validated. If you use your own images, you will want to set this to $True.</td></tr> <tr> <td valign="top"> <p align="left">VMRootPath</p></td> <td valign="top"> <p align="center">NO</p></td> <td valign="top"> <p align="center">String</p></td> <td valign="top"> <p align="left">$PWD</p></td></tr> <tr> <td colspan="4">This is the path to download the zip image to and create the VM image folder where the VM will reside. By default it will use the Current Working Directory.</td></tr></tbody></table> <h3>OS / IEVersion</h3> <p>This is the version of the OS that you want hosted and the version of IE you want with it. Here are the supported values:</p> <table cellspacing="0" cellpadding="2" width="100%" border="1"> <tbody> <tr> <th valign="top" width="9%">&nbsp;</td> <th valign="top" width="9%"> <p align="center"><strong>IE 6</strong></p></td> <th valign="top" width="9%"> <p align="center"><strong>IE 7</strong></p></td> <th valign="top" width="9%"> <p align="center"><strong>IE 8</strong></p></td> <th valign="top" width="9%"> <p align="center"><strong>IE 9</strong></p></td> <th valign="top" width="9%"> <p align="center"><strong>IE 10</strong></p></td> <th valign="top" width="9%"> <p align="center"><strong>IE 11</strong></p></td> <th valign="top" width="9%"> <p align="center"><strong>MS Edge</strong></p></td> <th valign="top" width="46%"> <p align="center"><strong>Requires 64-bit Emulation</strong></p></td></tr> <tr> <td valign="top"> <p align="center"><strong>XP</strong></p></td> <td valign="top"> <p align="center">X</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">X</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td></tr> <tr> <td valign="top"> <p align="center"><strong>Vista</strong></p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">X</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td></tr> <tr> <td valign="top"> <p align="center"><strong>Win7</strong></p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">X</p></td> <td valign="top"> <p align="center">X</p></td> <td valign="top"> <p align="center">X</p></td> <td valign="top"> <p align="center">X</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td></tr> <tr> <td valign="top"> <p align="center"><strong>Win8</strong></p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">X</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td></tr> <tr> <td valign="top"> <p align="center"><strong>Win8.1</strong></p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">X</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">X</p></td></tr> <tr> <td valign="top"> <p align="center"><strong>Win10</strong></p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">&nbsp;</p></td> <td valign="top"> <p align="center">X</p></td> <td valign="top"> <p align="center">X</p></td></tr></tbody></table> <p><h2><a href="https://github.com/camalot/psievm/releases">Download PSIEVM</a></h2></p> <img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/b0AAEQjf6Vw" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2015/10/simplify-standing-up-ie-virtual-machine.htmltag:blogger.com,1999:blog-3134018267377916851.post-41757524199042992692015-09-18T11:30:00.001-05:002015-09-18T12:05:28.172-05:00Droid Explorer 0.9.0.4 Released<p>This is a pretty big release for being a maintenance release. There are some bug fixes and a couple new features.</p> <p><strong>New Features:</strong></p> <p>Sqlite files are now edited using <a href="http://sqlitebrowser.org/" target="_blank">Sqlite Browser</a>. This tool is far more stable, and advanced than the tool that was included before in Droid Explorer. The way this tool works, the database that you want to query/modify will be copied to your PC. You can query or modify the data. Once you close the Sqlite Browser, it will ask you if you want to push the changes back to the device.</p> <p><img title="changes" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="changes" src="http://lh3.googleusercontent.com/-qqtBKh6Vkl0/Vfw8LY57SvI/AAAAAAAAu2w/ib0WmpK2Ru8/changes%25255B5%25255D.png?imgmax=800" width="392" height="326"> </p> <p>ScreenCast is now called Screen Monitor and now uses a fork of <a href="http://adakoda.github.io/android-screen-monitor/" target="_blank">Android Screen Monitor</a>. The fork just removes the need to have the user select the device, since Droid Explorer already knows the device.</p> <p><img title="monitor" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="monitor" src="http://lh3.googleusercontent.com/-xIKWK-F-NSo/Vfw8MPghSdI/AAAAAAAAu20/0t3Rz1XN_jE/monitor%25255B5%25255D.png?imgmax=800" width="380" height="687"> </p> <p>Screen Capture: This allow you to record your device screen for up to 3 minutes.</p> <p><img title="capture" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="capture" src="http://lh3.googleusercontent.com/-afsZlUirMwU/Vfw8MiqI5CI/AAAAAAAAu28/KZx6s9iLzX4/capture%25255B5%25255D.png?imgmax=800" width="533" height="303"> </p> <p>Rotation is done when you copy the video to the PC. This uses FFMpeg to do the rotation.</p> <p><strong>Sample video:</strong><br><a href="https://youtu.be/yiEKyAlKS9g"><img src="http://i.imgur.com/TsYK0l4.png"></a> </p> <p><strong>Changelog:</strong></p> <ul> <li>Fixed the install apk shortcut to default to install. [workitem:17512] <li>Added screen capture functionality. The Plugin will show up if the connected device supports screen capture. [workitem:17417] <li>Tweaked the SaveFileDialog, OpenFileDialog, &amp; FileDialog to better support an 'initial directory'. <li>Tweaked the way the plugin toolstrips are created to hopefully support reloading when connecting to another device.&nbsp; <li>Fixed launching screenshot from the shortcut or jumplist item. [workitem:17416] <li>Replaced the JNLP with a more active developed androidscreencast and launch a jar file directly. <li>Changed the window of the ScreenRecorder to be fixed. <li>Updated the InstallDialog to make use of the PluginHost which provides device and other information. <li>Fixed the regex for checking if an app was installed successfully and fixed the regex for getting the permissions <li>Fixed the device backup so it launches correctly from the shortcut. <li>Added Sqlite Browser to replace the feature lacking sqlite explorer that was part of this project. See <a href="http://sqlitebrowser.org/" target="_blank">http://sqlitebrowser.org/</a> for info on Sqlite Browser. <li>TransferDialog now usable by plugins. [workitem:17521] <li>Consolidation of some string resources in to the Global App Resources <li>Default the file name when saving a screenshot to be 'screenshot-{yyyy-MM-dd-hhmmss}.png' [workitem:17516] <li>All PluginForms now require IPluginHost to be passed in.&nbsp; <li>Updated image resources for the screenshot plugin <li>added toolbar item to screenshot to "edit in default application" <li>removed SqliteEditForm since it isn't used any more. <li>fixed the saving size of the screenshot. It will save as the original source size [workitem:17517] <li>Added ability to rotate screen capture video 90/180/270 degrees when copying to pc. uses ffmpeg to rotate video. [workitem:17519] <li>Added option to delete capture file after copy to pc [workitem:17525] <li>set a default filename on '/sdcard/' for screen capture [workitem:17523] <li>Changed when disconnected from device that the 'connect to device' button is not disabled. [workitem:17528] <li>Fixed crash when the monitor service is not running and you disconnect from the device because the ADB daemon shuts down and status cannot be retrieved. <li>Added link to WiFi ADB app on the remote connect dialog. This will help people enable wifi adb. [workitem:17533] <li>Added method to IPlugin interface to initialize the plugin. This can be used to set up files on the device before the plugin executes. <li>Removed androidscreencast, switched to using android screen monitor fork (<a href="https://github.com/camalot/android-screen-monitor" target="_blank">https://github.com/camalot/android-screen-monitor</a>). The fork allows the device ID to be passed as an argument. This way the user does not have to select the device again. <li>Changed the call to launch the jar for screen monitor to call "java.exe", before it was just "java" and that was causing issues. [workitem:17531] <li>Added more logging during initialization to help with debugging start up issues. [workitem:17527] <li>Set the toolstrip renderer on screenshot plugin to be the same that is used elsewhere. <li>Added functions for Forward &amp; Reverse to be used with the port manager <li>Added option to restart ADB server on the connection dialog </li></ul> <p>&nbsp;</p> <p>&nbsp;</p> <p><a href="https://de.codeplex.com/downloads/get/1487566"><img title="32bit-128" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="32bit-128" src="http://lh3.googleusercontent.com/-2VzJ_jAeTF0/Ve83XQXYRiI/AAAAAAAAusM/vxwJLvYMd_0/32bit-128%25255B4%25255D.png?imgmax=800" width="128" height="128"></a>&nbsp; <a href="https://de.codeplex.com/downloads/get/1487567"><img title="64bit-128" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="64bit-128" src="http://lh3.googleusercontent.com/-OgEA4fM6gKc/Ve83YHJoYtI/AAAAAAAAusU/dNu21eZAAsU/64bit-128%25255B4%25255D.png?imgmax=800" width="128" height="128"></a></p> <img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/6luA924t5QY" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2015/09/droid-explorer-0904-released.htmltag:blogger.com,1999:blog-3134018267377916851.post-34566863908480282792015-09-13T20:33:00.001-05:002015-09-13T20:33:17.838-05:00New feature coming to Droid Explorer<p>I want to give a sneak peak of a feature in the upcoming release of Droid Explorer called Device Screen Capture. On devices that support it, you will be able to record screen activity for up to 3 minutes.</p> <p><img src="http://i.imgur.com/AP0lt7W.png"></p> <p>If you choose to copy to your computer after capture, you will be able to also apply rotation to the video.</p> <p>&nbsp;</p> <p>Here is a sample video with the following settings:</p> <ul> <li>Length: 20 seconds</li> <li>Bitrate: 4Mbps</li> <li>Size: 1440x2560</li> <li>Rotate: 90 degrees</li></ul> <p><a href="https://youtu.be/yiEKyAlKS9g" target="_blank"><img src="http://i.imgur.com/TsYK0l4.png"></a></p> <img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/fF4DY6cB5KI" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2015/09/new-feature-coming-to-droid-explorer.htmltag:blogger.com,1999:blog-3134018267377916851.post-42201508610242424262015-09-08T14:30:00.001-05:002015-09-08T15:56:29.921-05:00Droid Explorer 0.9.0.3 Released<p>There is a new release of Droid Explorer that is <a href="http://de.bit13.com/releases/go/" target="_blank">now available for download</a>. This release is mainly some bug fixes. The most important is fixing support for the changes to ADB where the feature ‘status-window’ was removed. Droid Explorer used this feature to monitor the status of the connected devices. The removal of this required a little reworking of the monitoring, but at this point I think I am happy with the new functionality. </p> <p>Full change log is available on <a href="http://de.bit13.com/releases/version/0.9.0.3/" target="_blank">Droid Explorer Site</a>:</p> <ul> <li>updated the location of the device images to use the program data directory. This is mainly for the service because a normal user, on windows 10, will not have access to read the icon file. <li>updated to use Visual Studio 2015 <li>updated to .net 4.5.2 <li>updated to use Wix 3.10 - it will try to use the installed version of wix, if it has created the WIX environment variable <li>changed the position of the Ask for Help button on windows 8+ devices to account for the larger buttons (on windows 10) <li>fixed when looking for the build tools versions it will only take directories in to account that match a version string regex. <li>Reworked the 'status-window' to use existing functionality in ADB so a different binary does not have to be distributed with Droid Explorer. <li>Updated some of the plugins that launch tools. It now checks for the tools, if it can't find them, they will not show up. <li>Updated the application manifest files to support current features in windows. <li>Added check to see if user (or user policy) has recent document tracking disabled. If it is, do not attempt to create jump lists.</li></ul> <p>&nbsp;</p> <p><a href="http://de.codeplex.com/downloads/get/1483548"><img title="32bit-128" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="32bit-128" src="http://lh3.googleusercontent.com/-2VzJ_jAeTF0/Ve83XQXYRiI/AAAAAAAAusM/vxwJLvYMd_0/32bit-128%25255B4%25255D.png?imgmax=800" width="128" height="128"></a>&nbsp; <a href="http://de.codeplex.com/downloads/get/1483552"><img title="64bit-128" style="border-left-width: 0px; border-right-width: 0px; border-bottom-width: 0px; display: inline; border-top-width: 0px" border="0" alt="64bit-128" src="http://lh3.googleusercontent.com/-OgEA4fM6gKc/Ve83YHJoYtI/AAAAAAAAusU/dNu21eZAAsU/64bit-128%25255B4%25255D.png?imgmax=800" width="128" height="128"></a></p> <img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/5GvggTKXm0k" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2015/09/droid-explorer-0903-released.htmltag:blogger.com,1999:blog-3134018267377916851.post-79830374727552315942015-08-11T20:39:00.001-05:002015-08-11T20:41:05.651-05:00Growl for Windows Display<p>I have been a user of Growl for Windows for a long time. I recently upgraded to Windows 10 and installed Growl, like I usually do. I liked the way the new notifications in Windows 10 looked and wanted that look for Growl so I created a Windows 10 Style Display for Growl.</p> <p>&nbsp;</p> <p>Right now, there is really only one option to set, and that is where you would like it to show up on screen.</p> <p><img title="settings" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="settings" src="http://lh3.googleusercontent.com/-DP8mOb3mpsU/VcqjzcmBrpI/AAAAAAAAuJo/GfvbCeMbank/settings%25255B5%25255D.png?imgmax=800" width="669" height="419"> </p> <p>Here is a preview of the Display.</p> <p><img title="preview" style="border-top: 0px; border-right: 0px; border-bottom: 0px; border-left: 0px; display: inline" border="0" alt="preview" src="http://lh3.googleusercontent.com/-WP6CxM-rQVs/Vcqj0gHCpMI/AAAAAAAAuJw/xvPXcgkeZTw/preview%25255B5%25255D.png?imgmax=800" width="360" height="100"> </p> <p>You can <a href="https://github.com/camalot/scratch/releases/download/G4W10-v1.0.1/Win10.zip" target="_blank">manually install by downloading from Github</a> (source is also available), or you can <a href="growl://display*https://raw.githubusercontent.com/camalot/scratch/master/growl/displays/Windows10GrowlDisplay/Deploy/Windows10.xml">automatically install to Growl for Windows</a>.</p> <img src="http://feeds.feedburner.com/~r/Adb-dShellRebootRecovery/~4/B1t5nbK-kZc" height="1" width="1" alt=""/>Ryan Conradhttps://plus.google.com/112900757733721136123noreply@blogger.com0http://blog.twimager.com/2015/08/growl-for-windows-display.html