<div dir="ltr">Hi Tony,<div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><span style="font-size:12.8px">When the activity maintainer and the developer agree that a new version of an activity is ready for release, all that is needed is for the activity maintainer to build the bundle and store it in the appropriate add-on directory. ASLO. </span><br style="font-size:12.8px"><br style="font-size:12.8px"><span style="font-size:12.8px">ASLO does not need any notification since each time a user clicks on an activity, ASLO finds the latest version and extracts the </span><a href="http://activity.info/" target="_blank" style="font-size:12.8px">activity.info</a><span style="font-size:12.8px"> to prepare the detail page. If the user clicks on download, the bundle is downloaded by its url. </span><br style="font-size:12.8px"><br style="font-size:12.8px"><span style="font-size:12.8px">Note: this model makes ASLO essentially bullet-proof since the only actions available are to browse activities and select one for download. The access is read-only. At the same time ASLO does not need a backend requiring adminstration.</span></blockquote><div><br></div><div>This significantly reduces the development effort. So, does that mean the developer have to specify download url in the <a href="http://activity.info">activity.info</a> ?</div><div>What do you think about the rest of process. Is it the best practice to use background worker to fetch <a href="http://activity.info">activity.info</a> ?</div><div><br></div><div>Thanks,</div><div>Jatin Dhankhar </div></div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, May 29, 2017 at 6:21 PM, Tony Anderson <span dir="ltr"><<a href="mailto:tony_anderson@usa.net" target="_blank">tony_anderson@usa.net</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div text="#000000" bgcolor="#FFFFFF">
<div class="m_4767995965331274873moz-cite-prefix">Hi Jatin,<span class=""><br>
<br>
<br>
<br>
Release is made into repo -> Webhook is triggered ->
Response is parsed -> A worker is spawned to handle building
(inside docker or in a worker thread) -> <br>
Once bundle is available, <a href="http://activity.info" target="_blank">activity.info</a> is parsed ->
Parsed activity is pushed to message queue -> Message queue is
used to update index of available activities <br>
<br></span>
When the activity maintainer and the developer agree that a new
version of an activity is ready for release, all that is needed is
for the activity maintainer to build the bundle and store it in
the appropriate add-on directory. ASLO. <br>
<br>
ASLO does not need any notification since each time a user clicks
on an activity, ASLO finds the latest version and extracts the
<a href="http://activity.info" target="_blank">activity.info</a> to prepare the detail page. If the user clicks on
download, the bundle is downloaded by its url. <br>
<br>
Note: this model makes ASLO essentially bullet-proof since the
only actions available are to browse activities and select one for
download. The access is read-only. At the same time ASLO does not
need a backend requiring adminstration.<span class="HOEnZb"><font color="#888888"><br>
<br>
Tony</font></span><div><div class="h5"><br>
<br>
On 05/28/2017 06:43 PM, Jatin Dhankhar wrote:<br>
</div></div></div><div><div class="h5">
<blockquote type="cite">
<div dir="ltr">Hi,
<div><br>
</div>
<div>I am using webhooks for notifying the server (for release
events only). At first I was thinking to clone the whole repo
but it is possible to access a single file via Github API [1]
which ultimately hands you a json including raw url to
download from.</div>
<div>After which it can be parsed to download the file but only
limitation is we cannot fetch a file greater than 1 MB.</div>
<div>Also it may take a long time to download/parse and
optionally generate bundle, such tasks can be handled via a
job management system like celery and when bundle is ready,
push the name of activity to pub/sub queue system (Is it an
effective approach ?), we can build them inside docker
containers as suggested by Samuel. </div>
<div>I have tested the hook and fetching, yet to test building
inside Container and setting up Background workers. I aslo
used <a href="https://www.iron.io/" target="_blank">https://www.iron.io/</a> at
a previous project for background workers and it was good but
it is expensive and may cause vendor lock-in. </div>
<div><br>
</div>
<div>It would be cool to use a CDN (either in-house or third
party like SourceForge or Bintray) to distribute the built
activities or we can push to <a href="http://activities.sugarlabs.org" target="_blank">activities.sugarlabs.org</a>. </div>
<div><br>
</div>
<div>So process so far would be </div>
<div><br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Release
is made into repo -> Webhook is triggered -> Response is
parsed -> A worker is spawned to handle building (inside
docker or in a worker thread) -> Once bundle is available,
<a href="http://activity.info" target="_blank">activity.info</a>
is parsed -> Parsed activity is pushed to message queue
-> Message queue is used to update index of available
activities </blockquote>
<div> </div>
<div>If something is missing/wrong, kindly guide me in the right
direction. </div>
<div>I have not configured the server properly yet, but it will
be accessible at <a href="http://aslo-dev.jatindhankhar.in" target="_blank">aslo-dev.jatindhankhar.in</a>,
right now serving on port 5000.</div>
<div>Thanks,</div>
<div><br>
</div>
<div>Jatin Dhankhar</div>
<div><br>
</div>
<div> </div>
<div> </div>
<div><br>
</div>
<div><br>
</div>
<div>[1] <a href="https://developer.github.com/v3/repos/contents/" target="_blank">https://developer.github.<wbr>com/v3/repos/contents/</a></div>
</div>
<div class="gmail_extra"><br>
<div class="gmail_quote">On Sat, May 27, 2017 at 9:32 PM, Jatin
Dhankhar <span dir="ltr"><<a href="mailto:dhankhar.jatin@gmail.com" target="_blank">dhankhar.jatin@gmail.com</a>></span>
wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="ltr"><span>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div style="font-size:12.8px">would suggest, if Walter
and Tony are agree, to start preparing a proof of
concept. Basically do the following:</div>
<div style="font-size:12.8px">(1) Clone some sugar
activity to your GH account like TurtleArt. Enable
webhooks for this repo and send events to your
server at some URL (like <a href="http://aslo.jatindhankhar.in/hook" target="_blank">http://aslo.jatindhankha<wbr>r.in/hook</a>)
when a new release has been done.</div>
<div style="font-size:12.8px">(2) Parse this payload
and get all the information from the <a href="http://activity.info/" target="_blank">activity.info</a> file
(you might look SamP's code). Please tell us what
might be missing for the site (comparing current
ASLO and ASLOv2) so we can think how to fill those
missing info. Please check this carefully.</div>
<div style="font-size:12.8px">(3) Build the bundle
inside a Docker container (no need for hacks). Just
simple docker container and no docker inside docker.
Probably bundles will be store at <a href="http://download.sugarlabs.org/activitities3/" target="_blank">download.sugarlabs.org/acti<wbr>vitities3/</a>.
But for now, just put in same server and some URL
like <a href="http://aslo.jatindhankhar.in/download/" target="_blank">http://aslo.jatindhankhar<wbr>.in/download/</a><path_to_app>. <wbr>Then
we will think how to sync this.</div>
<div style="font-size:12.8px">(4) Show a web page with
the listed app we can download this new generated
bundle.</div>
<div><br>
</div>
</blockquote>
<div><br>
</div>
</span>
<div>Okay, Will start work on this tomorrow. I was looking
at some Flask tutorials. Is it okay if I use Flask ?</div>
<div>Also wrote the weekly blog #3.</div>
<div><br>
</div>
<div>Thanks,</div>
<div>Jatin Dhankhar </div>
</div>
<div class="m_4767995965331274873HOEnZb">
<div class="m_4767995965331274873h5">
<div class="gmail_extra"><br>
<div class="gmail_quote">On Sat, May 27, 2017 at 7:48
PM, Samuel Cantero <span dir="ltr"><<a href="mailto:scanterog@gmail.com" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-abbreviated" href="mailto:scanterog@gmail.com" target="_blank">scanterog@gmail.com</a>></span>
wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<div dir="ltr">
<div class="gmail_extra">
<div class="gmail_quote"><span>On Fri, May 26,
2017 at 5:57 AM, Jatin Dhankhar <span dir="ltr"><<a href="mailto:dhankhar.jatin@gmail.com" target="_blank">dhankhar.jatin@gmail.com</a>></span>
wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div dir="ltr">Hi Sam,
<div><br>
</div>
<div>Thank you for very much for the
detailed explanation. Apart from
what you have already mentioned. I
checked the socialhelp module and it
fails to build since the package<i> python-cairosvg</i> is
not available in the Ubuntu repos
anymore[1].<br>
</div>
<div><br>
</div>
</div>
</blockquote>
<div><br>
</div>
</span>
<div>I think we don't have to worry about
integration with socialhelp.</div>
<span>
<div> </div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div dir="ltr">
<div>I also agree that new proposal[2]
has sane architecture and use of
Gtihub API over raw calls is
beneficial. </div>
</div>
</blockquote>
<div><br>
</div>
</span>
<div>I would suggest, if Walter and Tony are
agree, to start preparing a proof of
concept. Basically do the following:</div>
<div>(1) Clone some sugar activity to your
GH account like TurtleArt. Enable webhooks
for this repo and send events to your
server at some URL (like <a href="http://aslo.jatindhankhar.in/hook" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="http://aslo.jatindhankha" target="_blank">http://aslo.jatindhankha</a><wbr><a href="http://r.in/hook">r.in/hook</a>)
when a new release has been done.</div>
<div>(2) Parse this payload and get all the
information from the <a href="http://activity.info" target="_blank">activity.info</a> file
(you might look SamP's code). Please tell
us what might be missing for the site
(comparing current ASLO and ASLOv2) so we
can think how to fill those missing info.
Please check this carefully.</div>
<div>(3) Build the bundle inside a Docker
container (no need for hacks). Just simple
docker container and no docker inside
docker. Probably bundles will be store at
<a href="http://download.sugarlabs.org/activitities3/" target="_blank">download.sugarlabs.org/activit<wbr>ities3/</a>.
But for now, just put in same server and
some URL like <a href="http://aslo.jatindhankhar.in/download/" target="_blank">http://aslo.jatindhankhar.in/d<wbr>ownload/</a><path_to_app>. Then
we will think how to sync this.</div>
<div>(4) Show a web page with the listed app
we can download this new generated bundle.</div>
<div><br>
</div>
<div>If you need more info, just let us
know.</div>
<div><br>
</div>
<div>Best,</div>
<div><br>
</div>
<div>Sam C.</div>
<div>
<div class="m_4767995965331274873m_-8009501003666624965h5">
<div><br>
</div>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div dir="ltr">
<div><br>
</div>
<div><br>
</div>
<div>Thanks, </div>
<div>Jatin Dhankhar</div>
<div><br>
</div>
<div>[1] <a href="https://github.com/samdroid-apps/aslo/blob/master/socialhelp/Dockerfile#L20" target="_blank">https://github.com/samdroi<wbr>d-apps/aslo/blob/master/social<wbr>help/Dockerfile#L20</a></div>
<div>[2] <a href="https://goo.gl/VEIzCr" target="_blank">https://goo.gl/VEIzCr</a></div>
<div>
<div class="m_4767995965331274873m_-8009501003666624965m_-6349898199447121431gmail-h5">
<div class="gmail_extra"><br>
<div class="gmail_quote">On
Fri, May 26, 2017 at 6:33
AM, Tony Anderson <span dir="ltr"><<a href="mailto:tony@olenepal.org" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-abbreviated" href="mailto:tony@olenepal.org" target="_blank">tony@olenepal.org</a>></span>
wrote:<br>
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<div bgcolor="#FFFFFF">
<div class="m_4767995965331274873m_-8009501003666624965m_-6349898199447121431gmail-m_7882053398874099122m_-687307678483002188m_3776250212530713294moz-cite-prefix">Hi
Sam<br>
<br>
Great work! I agree
with you completely. <br>
<span class="m_4767995965331274873m_-8009501003666624965m_-6349898199447121431gmail-m_7882053398874099122m_-687307678483002188HOEnZb"><font color="#888888"> <br>
Tony</font></span>
<div>
<div class="m_4767995965331274873m_-8009501003666624965m_-6349898199447121431gmail-m_7882053398874099122m_-687307678483002188h5"><br>
<br>
On 05/26/2017
03:02 AM, Samuel
Cantero wrote:<br>
</div>
</div>
</div>
<div>
<div class="m_4767995965331274873m_-8009501003666624965m_-6349898199447121431gmail-m_7882053398874099122m_-687307678483002188h5">
<blockquote type="cite">
<div dir="ltr">
<div>Hi guys!</div>
<div><br>
</div>
I have review
Sam's code and
this is what
I've found so
far:
<div><br>
</div>
<div>1.
Initially, all
activities
shown in the
web interface
are read/parse
from the JSON
files [1][2]
uploaded to: <a class="m_4767995965331274873m_-8009501003666624965m_-6349898199447121431gmail-m_7882053398874099122m_-687307678483002188m_3776250212530713294moz-txt-link-freetext" href="https://github.com/samdroid-apps/sugar-activities" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdroi" target="_blank">https://github.com/samdroi</a><wbr>d-apps/sugar-activities.
Using a repo
as a
activities
registry has
advantages and
disadvantages.</div>
<div><br>
</div>
<div>2. The main
web app spawns
a thread [3]
which polls a
kafka message
queue
(consumer) in
order to get
the new
messages
(GitHub
payloads) [4]
inserted into
the message
queue by
events sent to
<a href="https://hook.sugarlabs.org/hook" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://hook.sugarlabs.org/hoo" target="_blank">https://hook.sugarlabs.org/hoo</a><wbr>k.</div>
<div><br>
</div>
<div>BUT this
only looks for
changes (GH
push events)
to
sugar-activities
repo. This
allows to
update the
activity
information in
the web
interface
(version,
download link,
etc) as well
as to
introduce new
activities
[5].</div>
<div><br>
</div>
<div>You can
check how the
GH payload is
processed
here: [6]. In
short, it
parses the GH
payload, gets
the repo URL
and publishes
some "message"
to the kafka
queue (or
bus), i.e act
as a kafka
producer so it
can be read
later by [4]. </div>
<div><br>
</div>
<div>This
"message"
generated at
[7] is just a
simple dict
(json) with
two data:
github repo
URL and
bundle_id. The
activity <i>bundle_id</i> is
gotten from
the <i><a href="http://activity.info" target="_blank">activity.info</a></i> file
(it doesn't
use GH API,
just use raw
access and
always master
branch).</div>
<div><br>
</div>
<div>3. Let's
review now the
bot's part so
we can
understand the
whole
workflow.
Inside repo we
can find two
folders: bot
and
bot-master. </div>
<div><br>
</div>
<div><i>Bot-master</i> is
a kafka
consumer for
the topic
"org.sugarlabs.aslo-changes"
[8]. This
topic is
different for
the one used
at [4]
(org.sugarlabs.hook).
Simplifying,
every new
topic is a new
message bus.
As a consumer,
it needs to
receive some
message to
process first.</div>
<div><br>
</div>
<div>This is
where <i>bot</i>
comes in. <i>Bot</i>
is the
producer for
the topic
"org.sugarlabs.aslo-changes"
(as expected)
[9] but also
it is a
consumer for
the topic
"org.sugarlabs.hook"
[10]. This
means that
when a new
payload is
sent to <a href="http://hook.sugarlabs.org/hook" target="_blank">hook.sugarlabs.org/hook</a>,
<i>bot</i>
also gets the
message
"produced" at
[7] (repo URL,
bundle_id) and
based on that
message does
the following:</div>
<div>(a) clone
the repo
(master
branch) [11];
- this is of
course the
activity repo
from some GH
account - no
centralized
repositories
because devs
registers the
webhooks as
explains
here: <a href="https://github.com/samdroid-apps/sugar-activities/blob/master/README.md" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdr" target="_blank">https://github.com/samdr</a><wbr>oid-apps/sugar-activities/blob<wbr>/master/README.md.</div>
<div>(b) get the
json file
using the
bundle_id
from <a class="m_4767995965331274873m_-8009501003666624965m_-6349898199447121431gmail-m_7882053398874099122m_-687307678483002188m_3776250212530713294moz-txt-link-freetext" href="http://raw.githubusercontent.com/SAMdroid-apps/sugar-activities/master/" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="http://raw.githubusercont" target="_blank">http://raw.githubusercont</a><wbr><a href="http://ent.com/SAMdroid-apps/sugar-ac">ent.com/SAMdroid-apps/sugar-ac</a><wbr>tivities/master/{bundle_id}.js<wbr>on.
Again, no GH
API [12];</div>
<div>(c) Parse <a href="http://activity.info" target="_blank">activity.info</a>
from cloned
repo. There
are many
useful things
here like:
check if
activity is
GTK3, if has
old toolbars
and check
minimal sugar
version
supported
[13];</div>
<div>(d) Get the
images from
cloned repo:
screenshots
and icons
[14];</div>
<div>(e)
generates
bundle
filename (xo
name) and set
some extra
data in <a href="http://activity.info" target="_blank">activity.info</a>; </div>
<div>(f)
generate
(compile) the
bundle (xo
file) [15]. As
you can see
[16], this
code calls the
docker
container
"samdroid/activity-build".
I suppose this
has been
generated
previously
using [17].
Because SamP
was running
this bot
inside Docker
and was
building the
image using
docker again,
he needed the
wrapdocker
file [18].</div>
<div><br>
</div>
<div>Once the
bundle has
been
generated, it
produces the
following
message [19]
for
bot-master,
which in turns
save the
bundle [20]
and update
sugar-activities
repo with the
new
releases/version
and other
needed
information
(as new
translations)
- example at
[21]. Yes,
this process
also manages
i18n.</div>
<div><br>
</div>
<div><b>My
conclusion</b></div>
<div><b><br>
</b></div>
<div>After
reviewing
code, I still
believe the
proposal I've
sent before
[22] is the
simplest way
to manage
this. Of
course, maybe
we can
optimize some
stuff along
the way. Maybe
we can add the
chance to
upload bundles
file (XOs)
manually as
well, so we
don't "remove"
devs the
chance to
upload theirs
XOs.</div>
<div><br>
</div>
<div>Reading
SamP who wrote
ASLO2, I'm
still even
more convinced
[22] is the
right way. He
basically
described this
proposed
solution in
previous
email: <a href="http://lists.sugarlabs.org/archive/iaep/2017-May/019761.html" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="http://lists.sugarlabs" target="_blank">http://lists.sugarlabs</a>.<wbr>org/archive/iaep/2017-May/0197<wbr>61.html.
Of course,
ASLO2 gives us
a lot of code
we can reuse
for ASLO3.</div>
<div><br>
</div>
<div>Best,</div>
<div><br>
</div>
<div>SamC.</div>
<div><br>
</div>
<div>[1] <a href="https://github.com/samdroid-apps/aslo/blob/master/web/main.py#L112" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdroi" target="_blank">https://github.com/samdroi</a><wbr>d-apps/aslo/blob/master/web/ma<wbr>in.py#L112</div>
<div>[2] <a href="https://github.com/samdroid-apps/aslo/blob/master/web/backend.py#L32" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdroi" target="_blank">https://github.com/samdroi</a><wbr>d-apps/aslo/blob/master/web/ba<wbr>ckend.py#L32</div>
<div>[3] <a href="https://github.com/samdroid-apps/aslo/blob/master/web/main.py#L114" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdroi" target="_blank">https://github.com/samdroi</a><wbr>d-apps/aslo/blob/master/web/ma<wbr>in.py#L114</div>
<div>[4] <a href="https://github.com/samdroid-apps/aslo/blob/master/web/main.py#L92" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdroi" target="_blank">https://github.com/samdroi</a><wbr>d-apps/aslo/blob/master/web/ma<wbr>in.py#L92</div>
<div>[5] <a href="https://github.com/samdroid-apps/aslo/blob/master/web/main.py#L107" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdroi" target="_blank">https://github.com/samdroi</a><wbr>d-apps/aslo/blob/master/web/ma<wbr>in.py#L107</div>
<div>[6] <a href="https://github.com/samdroid-apps/aslo/blob/master/hookin/main.py#L12" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdroi" target="_blank">https://github.com/samdroi</a><wbr>d-apps/aslo/blob/master/hookin<wbr>/main.py#L12</div>
<div>[7] <a href="https://github.com/samdroid-apps/aslo/blob/master/hookin/backend.py#L82" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdroi" target="_blank">https://github.com/samdroi</a><wbr>d-apps/aslo/blob/master/hookin<wbr>/backend.py#L82</div>
<div>[8] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot-master/main.py#L77" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdroi" target="_blank">https://github.com/samdroi</a><wbr>d-apps/aslo/blob/master/bot-ma<wbr>ster/main.py#L77</div>
<div>[9] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot/main.py#L40" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdroi" target="_blank">https://github.com/samdroi</a><wbr>d-apps/aslo/blob/master/bot/ma<wbr>in.py#L40</div>
<div>[10] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot/main.py#L36" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/bot/m<wbr>ain.py#L36</div>
<div>[11] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot/main.py#L64" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/bot/m<wbr>ain.py#L64</div>
<div>[12] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot/img.py#L31" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/bot/i<wbr>mg.py#L31</div>
<div>[13] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot/props.py#L117" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/bot/p<wbr>rops.py#L117</div>
<div>[14] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot/img.py#L55" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/bot/i<wbr>mg.py#L55</div>
<div>[15] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot/build.py#L74" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/bot/b<wbr>uild.py#L74</div>
<div>[16] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot/build.py#L84" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/bot/b<wbr>uild.py#L84</div>
<div>[17] <a href="https://github.com/samdroid-apps/aslo/blob/master/activity-build-docker/Dockerfile" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/activ<wbr>ity-build-docker/Dockerfile</div>
<div>[18] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot/wrapdocker" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/bot/w<wbr>rapdocker</div>
<div>[19] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot/main.py#L100" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/bot/m<wbr>ain.py#L100</div>
<div>[20] <a href="https://github.com/samdroid-apps/aslo/blob/master/bot-master/main.py#L47" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/aslo/blob/master/bot-m<wbr>aster/main.py#L47</div>
<div>[21] <a href="https://github.com/samdroid-apps/sugar-activities/commit/8de09150934502a845b5f33e6031e5ebd60e4d94" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://github.com/samdro" target="_blank">https://github.com/samdro</a><wbr>id-apps/sugar-activities/commi<wbr>t/8de09150934502a845b5f33e6031<wbr>e5ebd60e4d94</div>
<div>[22] <a href="https://goo.gl/VEIzCr" target="_blank"></a><a class="m_4767995965331274873moz-txt-link-freetext" href="https://goo.gl/VEIzCr" target="_blank">https://goo.gl/VEIzCr</a></div>
</div>
</blockquote>
<br>
</div>
</div>
</div>
</blockquote>
</div>
<br>
</div>
</div>
</div>
</div>
</blockquote>
</div>
</div>
</div>
<br>
</div>
</div>
</blockquote>
</div>
<br>
</div>
</div>
</div>
</blockquote>
</div>
<br>
</div>
</blockquote>
<br>
</div></div></div>
</blockquote></div><br></div>