aboutsummaryrefslogtreecommitdiff
path: root/gerboweb
diff options
context:
space:
mode:
Diffstat (limited to 'gerboweb')
-rw-r--r--gerboweb/deploy/mirrorlist474
-rw-r--r--gerboweb/deploy/playbook.yml100
-rwxr-xr-xgerboweb/deploy/render.sh20
-rwxr-xr-xgerboweb/deploy/vector.sh18
-rw-r--r--gerboweb/gerboweb.cfg4
-rw-r--r--gerboweb/gerboweb.py147
-rw-r--r--gerboweb/job_processor.py40
-rw-r--r--gerboweb/job_queue.py88
-rw-r--r--gerboweb/templates/index.html86
9 files changed, 977 insertions, 0 deletions
diff --git a/gerboweb/deploy/mirrorlist b/gerboweb/deploy/mirrorlist
new file mode 100644
index 0000000..a2fd58c
--- /dev/null
+++ b/gerboweb/deploy/mirrorlist
@@ -0,0 +1,474 @@
+##
+## Arch Linux repository mirrorlist
+## Generated on 2017-06-06
+##
+
+## Worldwide
+#Server = https://archlinux.surlyjake.com/archlinux/$repo/os/$arch
+#Server = http://mirrors.evowise.com/archlinux/$repo/os/$arch
+Server = http://mirror.rackspace.com/archlinux/$repo/os/$arch
+
+## Australia
+#Server = https://mirror.aarnet.edu.au/pub/archlinux/$repo/os/$arch
+#Server = http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch
+#Server = http://ftp.iinet.net.au/pub/archlinux/$repo/os/$arch
+#Server = http://mirror.internode.on.net/pub/archlinux/$repo/os/$arch
+#Server = http://ftp.swin.edu.au/archlinux/$repo/os/$arch
+#Server = http://archlinux.uberglobalmirror.com/$repo/os/$arch
+
+## Austria
+#Server = http://mirror.digitalnova.at/archlinux/$repo/os/$arch
+#Server = http://mirror.easyname.at/archlinux/$repo/os/$arch
+#Server = http://mirror1.htu.tugraz.at/archlinux/$repo/os/$arch
+
+## Belarus
+#Server = http://ftp.byfly.by/pub/archlinux/$repo/os/$arch
+#Server = http://mirror.datacenter.by/pub/archlinux/$repo/os/$arch
+
+## Belgium
+#Server = http://archlinux.cu.be/$repo/os/$arch
+#Server = http://archlinux.mirror.kangaroot.net/$repo/os/$arch
+
+## Bosnia and Herzegovina
+#Server = http://burek.archlinux.ba/$repo/os/$arch
+#Server = http://archlinux.mirror.ba/$repo/os/$arch
+
+## Brazil
+#Server = http://br.mirror.archlinux-br.org/$repo/os/$arch
+#Server = http://archlinux.c3sl.ufpr.br/$repo/os/$arch
+#Server = http://linorg.usp.br/archlinux/$repo/os/$arch
+#Server = http://pet.inf.ufsc.br/mirrors/archlinux/$repo/os/$arch
+#Server = http://archlinux.pop-es.rnp.br/$repo/os/$arch
+
+## Bulgaria
+#Server = http://mirror.host.ag/archlinux/$repo/os/$arch
+#Server = http://mirrors.netix.net/archlinux/$repo/os/$arch
+#Server = http://mirror.telepoint.bg/archlinux/$repo/os/$arch
+#Server = http://mirrors.uni-plovdiv.net/archlinux/$repo/os/$arch
+#Server = https://mirrors.uni-plovdiv.net/archlinux/$repo/os/$arch
+
+## Canada
+#Server = http://mirror.cedille.club/archlinux/$repo/os/$arch
+#Server = http://archlinux.mirror.colo-serv.net/$repo/os/$arch
+#Server = http://mirror.csclub.uwaterloo.ca/archlinux/$repo/os/$arch
+#Server = https://mirror.csclub.uwaterloo.ca/archlinux/$repo/os/$arch
+#Server = http://mirror.frgl.pw/archlinux/$repo/os/$arch
+#Server = https://mirror.frgl.pw/archlinux/$repo/os/$arch
+#Server = http://mirror.its.dal.ca/archlinux/$repo/os/$arch
+#Server = http://muug.ca/mirror/archlinux/$repo/os/$arch
+#Server = https://muug.ca/mirror/archlinux/$repo/os/$arch
+#Server = http://archlinux.mirror.rafal.ca/$repo/os/$arch
+
+## Chile
+#Server = http://mirror.archlinux.cl/$repo/os/$arch
+
+## China
+#Server = http://mirrors.163.com/archlinux/$repo/os/$arch
+#Server = http://mirror.lzu.edu.cn/archlinux/$repo/os/$arch
+#Server = http://mirrors.neusoft.edu.cn/archlinux/$repo/os/$arch
+#Server = https://mirrors.skyshe.cn/archlinux/$repo/os/$arch
+#Server = http://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch
+#Server = https://mirrors.tuna.tsinghua.edu.cn/archlinux/$repo/os/$arch
+#Server = http://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch
+#Server = https://mirrors.ustc.edu.cn/archlinux/$repo/os/$arch
+#Server = http://mirrors.xjtu.edu.cn/archlinux/$repo/os/$arch
+#Server = https://mirrors.xjtu.edu.cn/archlinux/$repo/os/$arch
+#Server = http://mirrors.zju.edu.cn/archlinux/$repo/os/$arch
+
+## Colombia
+#Server = http://mirror.edatel.net.co/archlinux/$repo/os/$arch
+#Server = http://mirror.upb.edu.co/archlinux/$repo/os/$arch
+
+## Croatia
+#Server = http://archlinux.iskon.hr/$repo/os/$arch
+
+## Czech Republic
+#Server = http://mirror.dkm.cz/archlinux/$repo/os/$arch
+#Server = https://mirror.dkm.cz/archlinux/$repo/os/$arch
+#Server = http://ftp.fi.muni.cz/pub/linux/arch/$repo/os/$arch
+#Server = http://ftp.linux.cz/pub/linux/arch/$repo/os/$arch
+#Server = http://gluttony.sin.cvut.cz/arch/$repo/os/$arch
+#Server = https://gluttony.sin.cvut.cz/arch/$repo/os/$arch
+#Server = http://mirrors.nic.cz/archlinux/$repo/os/$arch
+#Server = http://ftp.sh.cvut.cz/arch/$repo/os/$arch
+#Server = https://ftp.sh.cvut.cz/arch/$repo/os/$arch
+#Server = http://mirror.vpsfree.cz/archlinux/$repo/os/$arch
+
+## Denmark
+#Server = http://mirrors.dotsrc.org/archlinux/$repo/os/$arch
+#Server = https://mirrors.dotsrc.org/archlinux/$repo/os/$arch
+#Server = http://ftp.klid.dk/ftp/archlinux/$repo/os/$arch
+#Server = http://mirror.one.com/archlinux/$repo/os/$arch
+#Server = https://mirror.one.com/archlinux/$repo/os/$arch
+
+## Ecuador
+#Server = http://mirror.cedia.org.ec/archlinux/$repo/os/$arch
+#Server = http://mirror.espoch.edu.ec/archlinux/$repo/os/$arch
+#Server = http://mirror.uta.edu.ec/archlinux/$repo/os/$arch
+
+## Finland
+#Server = http://arch.mirror.far.fi/$repo/os/$arch
+
+## France
+#Server = http://archlinux.de-labrusse.fr/$repo/os/$arch
+#Server = http://mirror.archlinux.ikoula.com/archlinux/$repo/os/$arch
+#Server = http://archlinux.vi-di.fr/$repo/os/$arch
+#Server = https://archlinux.vi-di.fr/$repo/os/$arch
+#Server = http://mirror.armbrust.me/archlinux/$repo/os/$arch
+#Server = https://mirror.armbrust.me/archlinux/$repo/os/$arch
+#Server = https://archlinux.ec-tech.fr/$repo/os/$arch
+#Server = http://fooo.biz/archlinux/$repo/os/$arch
+#Server = https://fooo.biz/archlinux/$repo/os/$arch
+#Server = http://mirror.gerhard.re/archlinux/$repo/os/$arch
+#Server = http://mirror.ibcp.fr/pub/archlinux/$repo/os/$arch
+#Server = http://mirror.lastmikoi.net/archlinux/$repo/os/$arch
+#Server = http://archlinux.mailtunnel.eu/$repo/os/$arch
+#Server = https://www.mailtunnel.eu/archlinux/$repo/os/$arch
+#Server = http://mir.archlinux.fr/$repo/os/$arch
+#Server = http://archlinux.mirrors.ovh.net/archlinux/$repo/os/$arch
+#Server = http://archlinux.mirror.pkern.at/$repo/os/$arch
+#Server = https://archlinux.mirror.pkern.at/$repo/os/$arch
+#Server = http://archlinux.polymorf.fr/$repo/os/$arch
+#Server = http://mirrors.standaloneinstaller.com/archlinux/$repo/os/$arch
+#Server = http://arch.tamcore.eu/$repo/os/$arch
+#Server = http://mirror.tyborek.pl/arch/$repo/os/$arch
+#Server = https://mirror.tyborek.pl/arch/$repo/os/$arch
+#Server = http://ftp.u-strasbg.fr/linux/distributions/archlinux/$repo/os/$arch
+#Server = https://mirror.wormhole.eu/archlinux/$repo/os/$arch
+#Server = http://arch.yourlabs.org/$repo/os/$arch
+
+## Germany
+#Server = http://mirror.23media.de/archlinux/$repo/os/$arch
+#Server = https://arch.32g.eu/$repo/os/$arch
+#Server = http://artfiles.org/archlinux.org/$repo/os/$arch
+#Server = https://fabric-mirror.vps.hosteurope.de/archlinux/$repo/os/$arch
+#Server = https://mirror.bethselamin.de/$repo/os/$arch
+#Server = http://mirror.euserv.net/linux/archlinux/$repo/os/$arch
+#Server = http://mirror.f4st.host/archlinux/$repo/os/$arch
+#Server = https://mirror.f4st.host/archlinux/$repo/os/$arch
+#Server = http://ftp.fau.de/archlinux/$repo/os/$arch
+#Server = https://ftp.fau.de/archlinux/$repo/os/$arch
+#Server = http://mirror.fluxent.de/archlinux/$repo/os/$arch
+#Server = https://mirror.fluxent.de/archlinux/$repo/os/$arch
+#Server = http://mirror.gnomus.de/$repo/os/$arch
+#Server = http://www.gutscheindrache.com/mirror/archlinux/$repo/os/$arch
+#Server = http://ftp.gwdg.de/pub/linux/archlinux/$repo/os/$arch
+#Server = http://mirror.hactar.xyz/$repo/os/$arch
+#Server = https://mirror.hactar.xyz/$repo/os/$arch
+#Server = http://archlinux.honkgong.info/$repo/os/$arch
+#Server = http://ftp.hosteurope.de/mirror/ftp.archlinux.org/$repo/os/$arch
+#Server = http://ftp-stud.hs-esslingen.de/pub/Mirrors/archlinux/$repo/os/$arch
+#Server = http://archlinux.mirror.iphh.net/$repo/os/$arch
+#Server = http://repo.itmettke.de/archlinux/$repo/os/$arch
+#Server = https://repo.itmettke.de/archlinux/$repo/os/$arch
+#Server = https://mirror.jankoppe.de/archlinux/$repo/os/$arch
+#Server = http://arch.jensgutermuth.de/$repo/os/$arch
+#Server = https://arch.jensgutermuth.de/$repo/os/$arch
+#Server = http://mirror.js-webcoding.de/pub/archlinux/$repo/os/$arch
+#Server = https://mirror.js-webcoding.de/pub/archlinux/$repo/os/$arch
+#Server = http://k42.ch/mirror/archlinux/$repo/os/$arch
+#Server = https://k42.ch/mirror/archlinux/$repo/os/$arch
+#Server = http://mirror.de.leaseweb.net/archlinux/$repo/os/$arch
+Server = https://mirror.de.leaseweb.net/archlinux/$repo/os/$arch
+#Server = http://mirror.loli.forsale/arch/$repo/os/$arch
+#Server = https://mirror.loli.forsale/arch/$repo/os/$arch
+#Server = http://mirror.metalgamer.eu/archlinux/$repo/os/$arch
+#Server = https://mirror.metalgamer.eu/archlinux/$repo/os/$arch
+#Server = http://mirror.michael-eckert.net/archlinux/$repo/os/$arch
+#Server = https://mirror.michael-eckert.net/archlinux/$repo/os/$arch
+#Server = http://mirrors.n-ix.net/archlinux/$repo/os/$arch
+#Server = https://mirrors.n-ix.net/archlinux/$repo/os/$arch
+#Server = http://mirror.netcologne.de/archlinux/$repo/os/$arch
+Server = https://mirror.netcologne.de/archlinux/$repo/os/$arch
+#Server = http://mirrors.niyawe.de/archlinux/$repo/os/$arch
+#Server = https://mirrors.niyawe.de/archlinux/$repo/os/$arch
+#Server = http://archlinux.nullpointer.io/$repo/os/$arch
+#Server = https://archlinux.nullpointer.io/$repo/os/$arch
+#Server = http://mirror.pseudoform.org/$repo/os/$arch
+#Server = https://mirror.pseudoform.org/$repo/os/$arch
+#Server = https://www.ratenzahlung.de/mirror/archlinux/$repo/os/$arch
+#Server = http://ftp.halifax.rwth-aachen.de/archlinux/$repo/os/$arch
+#Server = http://linux.rz.rub.de/archlinux/$repo/os/$arch
+#Server = http://mirror.selfnet.de/archlinux/$repo/os/$arch
+#Server = http://ftp.spline.inf.fu-berlin.de/mirrors/archlinux/$repo/os/$arch
+#Server = https://ftp.spline.inf.fu-berlin.de/mirrors/archlinux/$repo/os/$arch
+#Server = http://archlinux.thaller.ws/$repo/os/$arch
+#Server = https://archlinux.thaller.ws/$repo/os/$arch
+#Server = http://archlinux.thelinuxnetworx.rocks/$repo/os/$arch
+#Server = https://archlinux.thelinuxnetworx.rocks/$repo/os/$arch
+#Server = http://archmirror.tomforb.es/$repo/os/$arch
+#Server = https://archmirror.tomforb.es/$repo/os/$arch
+#Server = http://ftp.tu-chemnitz.de/pub/linux/archlinux/$repo/os/$arch
+#Server = http://mirror.ubrco.de/archlinux/$repo/os/$arch
+#Server = https://mirror.ubrco.de/archlinux/$repo/os/$arch
+#Server = http://ftp.uni-bayreuth.de/linux/archlinux/$repo/os/$arch
+#Server = http://ftp.uni-hannover.de/archlinux/$repo/os/$arch
+#Server = http://ftp.uni-kl.de/pub/linux/archlinux/$repo/os/$arch
+#Server = http://mirror.united-gameserver.de/archlinux/$repo/os/$arch
+#Server = http://mirror.vfn-nrw.de/archlinux/$repo/os/$arch
+#Server = https://mirror.vfn-nrw.de/archlinux/$repo/os/$arch
+
+## Greece
+#Server = http://ftp.cc.uoc.gr/mirrors/linux/archlinux/$repo/os/$arch
+#Server = http://foss.aueb.gr/mirrors/linux/archlinux/$repo/os/$arch
+#Server = https://foss.aueb.gr/mirrors/linux/archlinux/$repo/os/$arch
+#Server = http://mirrors.myaegean.gr/linux/archlinux/$repo/os/$arch
+#Server = http://ftp.ntua.gr/pub/linux/archlinux/$repo/os/$arch
+#Server = http://ftp.otenet.gr/linux/archlinux/$repo/os/$arch
+
+## Hong Kong
+#Server = http://arch-mirror.wtako.net/$repo/os/$arch
+#Server = https://arch-mirror.wtako.net/$repo/os/$arch
+
+## Hungary
+#Server = http://ftp.energia.mta.hu/pub/mirrors/ftp.archlinux.org/$repo/os/$arch
+#Server = http://archmirror.hbit.sztaki.hu/archlinux/$repo/os/$arch
+
+## Iceland
+#Server = http://mirror.system.is/arch/$repo/os/$arch
+#Server = https://mirror.system.is/arch/$repo/os/$arch
+
+## India
+#Server = http://mirror.cse.iitk.ac.in/archlinux/$repo/os/$arch
+#Server = http://ftp.iitm.ac.in/archlinux/$repo/os/$arch
+
+## Indonesia
+#Server = http://mirror.devilzc0de.org/archlinux/$repo/os/$arch
+#Server = http://mirror.poliwangi.ac.id/archlinux/$repo/os/$arch
+#Server = http://suro.ubaya.ac.id/archlinux/$repo/os/$arch
+
+## Iran
+#Server = http://repo.sadjad.ac.ir/arch/$repo/os/$arch
+#Server = https://repo.sadjad.ac.ir/arch/$repo/os/$arch
+
+## Ireland
+#Server = http://ftp.heanet.ie/mirrors/ftp.archlinux.org/$repo/os/$arch
+#Server = https://ftp.heanet.ie/mirrors/ftp.archlinux.org/$repo/os/$arch
+
+## Israel
+#Server = http://mirror.isoc.org.il/pub/archlinux/$repo/os/$arch
+
+## Italy
+#Server = http://archlinux.prometeolibero.eu/archlinux/$repo/os/$arch
+#Server = https://archlinux.prometeolibero.eu/archlinux/$repo/os/$arch
+#Server = https://archlinux.beccacervello.it/archlinux/$repo/os/$arch
+#Server = http://mi.mirror.garr.it/mirrors/archlinux/$repo/os/$arch
+#Server = http://mirrors.prometeus.net/archlinux/$repo/os/$arch
+#Server = http://archlinux.students.cs.unibo.it/$repo/os/$arch
+
+## Japan
+#Server = http://ftp.tsukuba.wide.ad.jp/Linux/archlinux/$repo/os/$arch
+Server = http://ftp.jaist.ac.jp/pub/Linux/ArchLinux/$repo/os/$arch
+
+## Kazakhstan
+#Server = http://mirror.neolabs.kz/archlinux/$repo/os/$arch
+
+## Latvia
+#Server = http://archlinux.koyanet.lv/archlinux/$repo/os/$arch
+
+## Lithuania
+#Server = http://mirrors.atviras.lt/archlinux/$repo/os/$arch
+#Server = https://mirrors.atviras.lt/archlinux/$repo/os/$arch
+
+## Luxembourg
+#Server = http://archlinux.mirror.root.lu/$repo/os/$arch
+
+## Macedonia
+#Server = http://arch.softver.org.mk/archlinux/$repo/os/$arch
+#Server = http://mirror.t-home.mk/archlinux/$repo/os/$arch
+#Server = https://mirror.t-home.mk/archlinux/$repo/os/$arch
+
+## Netherlands
+#Server = http://arch.apt-get.eu/$repo/os/$arch
+#Server = http://mirror.i3d.net/pub/archlinux/$repo/os/$arch
+#Server = https://mirror.i3d.net/pub/archlinux/$repo/os/$arch
+#Server = http://mirror.nl.leaseweb.net/archlinux/$repo/os/$arch
+#Server = https://mirror.nl.leaseweb.net/archlinux/$repo/os/$arch
+#Server = http://mirror.netrouting.net/archlinux/$repo/os/$arch
+#Server = http://ftp.nluug.nl/os/Linux/distr/archlinux/$repo/os/$arch
+#Server = http://ftp.snt.utwente.nl/pub/os/linux/archlinux/$repo/os/$arch
+#Server = http://archlinux.mirror.wearetriple.com/$repo/os/$arch
+#Server = https://archlinux.mirror.wearetriple.com/$repo/os/$arch
+
+## New Caledonia
+#Server = http://mirror.lagoon.nc/pub/archlinux/$repo/os/$arch
+#Server = http://archlinux.nautile.nc/archlinux/$repo/os/$arch
+
+## New Zealand
+#Server = https://mirror.smith.geek.nz/archlinux/$repo/os/$arch
+
+## Norway
+#Server = http://mirror.archlinux.no/$repo/os/$arch
+#Server = http://archlinux.uib.no/$repo/os/$arch
+#Server = http://mirror.neuf.no/archlinux/$repo/os/$arch
+#Server = https://mirror.neuf.no/archlinux/$repo/os/$arch
+
+## Philippines
+#Server = http://mirror.rise.ph/archlinux/$repo/os/$arch
+
+## Poland
+#Server = http://mirror.chmuri.net/archmirror/$repo/os/$arch
+#Server = http://arch.midov.pl/arch/$repo/os/$arch
+#Server = http://mirror.onet.pl/pub/mirrors/archlinux/$repo/os/$arch
+#Server = http://piotrkosoft.net/pub/mirrors/ftp.archlinux.org/$repo/os/$arch
+#Server = http://ftp.vectranet.pl/archlinux/$repo/os/$arch
+
+## Portugal
+#Server = http://glua.ua.pt/pub/archlinux/$repo/os/$arch
+#Server = https://glua.ua.pt/pub/archlinux/$repo/os/$arch
+#Server = http://ftp.rnl.tecnico.ulisboa.pt/pub/archlinux/$repo/os/$arch
+
+## Qatar
+#Server = http://mirror.qnren.qa/archlinux/$repo/os/$arch
+
+## Romania
+#Server = http://mirror.archlinux.ro/archlinux/$repo/os/$arch
+#Server = http://archlinux.mirrors.linux.ro/$repo/os/$arch
+#Server = http://mirrors.m247.ro/archlinux/$repo/os/$arch
+#Server = http://mirrors.pidginhost.com/arch/$repo/os/$arch
+
+## Russia
+#Server = http://mirror.aur.rocks/$repo/os/$arch
+#Server = https://mirror.aur.rocks/$repo/os/$arch
+#Server = http://mirror.rol.ru/archlinux/$repo/os/$arch
+#Server = https://mirror.rol.ru/archlinux/$repo/os/$arch
+#Server = http://mirror.yandex.ru/archlinux/$repo/os/$arch
+#Server = https://mirror.yandex.ru/archlinux/$repo/os/$arch
+
+## Serbia
+#Server = http://mirror.pmf.kg.ac.rs/archlinux/$repo/os/$arch
+
+## Singapore
+#Server = http://mirror.0x.sg/archlinux/$repo/os/$arch
+#Server = http://download.nus.edu.sg/mirror/arch/$repo/os/$arch
+
+## Slovakia
+#Server = http://mirror.lnx.sk/pub/linux/archlinux/$repo/os/$arch
+#Server = https://mirror.lnx.sk/pub/linux/archlinux/$repo/os/$arch
+#Server = http://tux.rainside.sk/archlinux/$repo/os/$arch
+
+## Slovenia
+#Server = http://archimonde.ts.si/archlinux/$repo/os/$arch
+#Server = https://archimonde.ts.si/archlinux/$repo/os/$arch
+
+## South Africa
+#Server = http://za.mirror.archlinux-br.org/$repo/os/$arch
+#Server = http://ftp.wa.co.za/pub/archlinux/$repo/os/$arch
+#Server = http://mirror.is.co.za/mirror/archlinux.org/$repo/os/$arch
+#Server = http://mirror.wbs.co.za/archlinux/$repo/os/$arch
+
+## South Korea
+#Server = http://ftp.kaist.ac.kr/ArchLinux/$repo/os/$arch
+#Server = http://mirror.premi.st/archlinux/$repo/os/$arch
+
+## Spain
+#Server = http://osl.ugr.es/archlinux/$repo/os/$arch
+#Server = http://sunsite.rediris.es/mirror/archlinux/$repo/os/$arch
+
+## Sweden
+#Server = http://ftp.acc.umu.se/mirror/archlinux/$repo/os/$arch
+#Server = https://ftp.acc.umu.se/mirror/archlinux/$repo/os/$arch
+#Server = http://archlinux.dynamict.se/$repo/os/$arch
+#Server = https://archlinux.dynamict.se/$repo/os/$arch
+#Server = http://ftp.lysator.liu.se/pub/archlinux/$repo/os/$arch
+#Server = https://ftp.lysator.liu.se/pub/archlinux/$repo/os/$arch
+#Server = http://ftp.myrveln.se/pub/linux/archlinux/$repo/os/$arch
+#Server = https://ftp.myrveln.se/pub/linux/archlinux/$repo/os/$arch
+#Server = https://mirror.osbeck.com/archlinux/$repo/os/$arch
+#Server = http://ftp.portlane.com/pub/os/linux/archlinux/$repo/os/$arch
+
+## Switzerland
+#Server = http://pkg.adfinis-sygroup.ch/archlinux/$repo/os/$arch
+#Server = https://pkg.adfinis-sygroup.ch/archlinux/$repo/os/$arch
+#Server = http://archlinux.puzzle.ch/$repo/os/$arch
+
+## Taiwan
+#Server = http://archlinux.cs.nctu.edu.tw/$repo/os/$arch
+#Server = http://shadow.ind.ntou.edu.tw/archlinux/$repo/os/$arch
+#Server = http://ftp.tku.edu.tw/Linux/ArchLinux/$repo/os/$arch
+#Server = http://ftp.yzu.edu.tw/Linux/archlinux/$repo/os/$arch
+
+## Thailand
+#Server = http://mirror.adminbannok.com/archlinux/$repo/os/$arch
+#Server = http://mirror.kku.ac.th/archlinux/$repo/os/$arch
+#Server = https://mirror.kku.ac.th/archlinux/$repo/os/$arch
+
+## Turkey
+#Server = http://ftp.linux.org.tr/archlinux/$repo/os/$arch
+
+## Ukraine
+#Server = http://archlinux.ip-connect.vn.ua/$repo/os/$arch
+#Server = https://archlinux.ip-connect.vn.ua/$repo/os/$arch
+#Server = http://mirrors.nix.org.ua/linux/archlinux/$repo/os/$arch
+#Server = https://mirrors.nix.org.ua/linux/archlinux/$repo/os/$arch
+
+## United Kingdom
+#Server = http://mirror.bytemark.co.uk/archlinux/$repo/os/$arch
+#Server = http://mirrors.manchester.m247.com/arch-linux/$repo/os/$arch
+#Server = http://www.mirrorservice.org/sites/ftp.archlinux.org/$repo/os/$arch
+#Server = http://arch.serverspace.co.uk/arch/$repo/os/$arch
+#Server = http://archlinux.mirrors.uk2.net/$repo/os/$arch
+
+## United States
+#Server = http://mirrors.acm.wpi.edu/archlinux/$repo/os/$arch
+#Server = http://mirrors.advancedhosters.com/archlinux/$repo/os/$arch
+#Server = http://mirrors.aggregate.org/archlinux/$repo/os/$arch
+#Server = http://ca.us.mirror.archlinux-br.org/$repo/os/$arch
+#Server = http://il.us.mirror.archlinux-br.org/$repo/os/$arch
+#Server = http://archlinux.surlyjake.com/archlinux/$repo/os/$arch
+#Server = http://arlm.tyzoid.com/$repo/os/$arch
+#Server = http://mirror.as65535.net/archlinux/$repo/os/$arch
+#Server = http://mirrors.cat.pdx.edu/archlinux/$repo/os/$arch
+#Server = http://mirror.cc.columbia.edu/pub/linux/archlinux/$repo/os/$arch
+#Server = http://arch.mirror.constant.com/$repo/os/$arch
+#Server = https://arch.mirror.constant.com/$repo/os/$arch
+#Server = http://cosmos.cites.illinois.edu/pub/archlinux/$repo/os/$arch
+#Server = http://mirror.cs.pitt.edu/archlinux/$repo/os/$arch
+#Server = http://mirror.cs.vt.edu/pub/ArchLinux/$repo/os/$arch
+#Server = http://mirror.epiphyte.network/archlinux/$repo/os/$arch
+#Server = https://mirror.epiphyte.network/archlinux/$repo/os/$arch
+#Server = http://mirror.es.its.nyu.edu/archlinux/$repo/os/$arch
+#Server = http://mirrors.gigenet.com/archlinux/$repo/os/$arch
+#Server = http://mirror.grig.io/archlinux/$repo/os/$arch
+#Server = https://mirror.grig.io/archlinux/$repo/os/$arch
+#Server = http://www.gtlib.gatech.edu/pub/archlinux/$repo/os/$arch
+#Server = http://mirror1.hackingand.coffee/arch/$repo/os/$arch
+#Server = http://mirror2.hackingand.coffee/arch/$repo/os/$arch
+#Server = http://mirror3.hackingand.coffee/arch/$repo/os/$arch
+#Server = http://mirror.htnshost.com/archlinux/$repo/os/$arch
+#Server = http://mirror.jmu.edu/pub/archlinux/$repo/os/$arch
+#Server = http://mirrors.kernel.org/archlinux/$repo/os/$arch
+#Server = https://mirrors.kernel.org/archlinux/$repo/os/$arch
+#Server = http://mirror.us.leaseweb.net/archlinux/$repo/os/$arch
+#Server = https://mirror.us.leaseweb.net/archlinux/$repo/os/$arch
+#Server = http://il.mirrors.linaxe.net/archlinux/$repo/os/$arch
+#Server = http://mirrors.liquidweb.com/archlinux/$repo/os/$arch
+#Server = http://arch.localmsp.org/arch/$repo/os/$arch
+#Server = https://arch.localmsp.org/arch/$repo/os/$arch
+#Server = http://mirror.lty.me/archlinux/$repo/os/$arch
+#Server = https://mirror.lty.me/archlinux/$repo/os/$arch
+#Server = http://mirrors.lug.mtu.edu/archlinux/$repo/os/$arch
+#Server = https://mirrors.lug.mtu.edu/archlinux/$repo/os/$arch
+#Server = http://mirror.math.princeton.edu/pub/archlinux/$repo/os/$arch
+#Server = http://mirror.metrocast.net/archlinux/$repo/os/$arch
+#Server = http://mirror.kaminski.io/archlinux/$repo/os/$arch
+#Server = https://mirror.kaminski.io/archlinux/$repo/os/$arch
+#Server = http://mirror.nexcess.net/archlinux/$repo/os/$arch
+#Server = http://mirrors.ocf.berkeley.edu/archlinux/$repo/os/$arch
+#Server = https://mirrors.ocf.berkeley.edu/archlinux/$repo/os/$arch
+#Server = http://ftp.osuosl.org/pub/archlinux/$repo/os/$arch
+#Server = http://arch.mirrors.pair.com/$repo/os/$arch
+#Server = http://mirrors.rit.edu/archlinux/$repo/os/$arch
+#Server = https://mirrors.rit.edu/archlinux/$repo/os/$arch
+#Server = http://mirrors.rutgers.edu/archlinux/$repo/os/$arch
+#Server = https://mirrors.rutgers.edu/archlinux/$repo/os/$arch
+#Server = https://mirrors.tuxns.net/archlinux/$repo/os/$arch
+#Server = http://mirror.umd.edu/archlinux/$repo/os/$arch
+#Server = http://mirror.vtti.vt.edu/archlinux/$repo/os/$arch
+#Server = http://mirrors.xmission.com/archlinux/$repo/os/$arch
+#Server = http://mirror.yellowfiber.net/archlinux/$repo/os/$arch
+
+## Vietnam
+#Server = http://f.archlinuxvn.org/archlinux/$repo/os/$arch
+#Server = http://mirror-fpt-telecom.fpt.net/archlinux/$repo/os/$arch
+
diff --git a/gerboweb/deploy/playbook.yml b/gerboweb/deploy/playbook.yml
new file mode 100644
index 0000000..a510772
--- /dev/null
+++ b/gerboweb/deploy/playbook.yml
@@ -0,0 +1,100 @@
+- name: Gerbolyze container setup playbook
+ hosts: localhost
+ connection: local
+ tasks:
+ - name: Download arch bootstrap image
+ get_url:
+ url: http://mirror.rackspace.com/archlinux/iso/2019.03.01/archlinux-bootstrap-2019.03.01-x86_64.tar.gz
+ dest: /tmp/arch-bootstrap.tar.xz
+ checksum: sha256:865c8a25312b663e724923eecf0dfc626f4cd621e2cfcb19eafc69a4fc666756
+
+ - name: Install host requisites
+ become: yes
+ dnf:
+ name: btrfs-progs,arch-install-scripts
+ state: latest
+
+ - name: Create container image file
+ become: yes
+ shell: truncate -s 4G /var/cache/gerbolyze_container.img
+ args:
+ creates: /var/cache/gerbolyze_container.img
+
+ - name: Create container image filesystem
+ become: yes
+ filesystem:
+ dev: /var/cache/gerbolyze_container.img
+ fstype: btrfs
+
+ - name: Create container image fstab entry
+ become: yes
+ mount:
+ src: /var/cache/gerbolyze_container.img
+ path: /var/cache/gerbolyze_container
+ state: mounted
+ fstype: btrfs
+ opts: loop
+
+ - name: Unpack bootstrap image
+ become: yes
+ unarchive:
+ remote_src: yes
+ src: /tmp/arch-bootstrap.tar.xz
+ dest: /var/cache/gerbolyze_container
+ extra_opts: --strip-components=1
+ creates: /var/cache/gerbolyze_container/etc
+
+ - name: Copy mirrorlist into container
+ become: yes
+ copy:
+ src: mirrorlist
+ dest: /var/cache/gerbolyze_container/etc/pacman.d/mirrorlist
+
+ - name: Copy render script
+ become: yes
+ copy:
+ src: render.sh
+ dest: /usr/local/sbin/gerbolyze_render.sh
+ mode: ug+x
+
+ - name: Copy vector script
+ become: yes
+ copy:
+ src: vector.sh
+ dest: /usr/local/sbin/gerbolyze_vector.sh
+ mode: ug+x
+
+ - name: Initialize container pacman keyring
+ become: yes
+ shell: arch-chroot /var/cache/gerbolyze_container pacman-key --init && arch-chroot /var/cache/gerbolyze_container pacman-key --populate archlinux
+ args:
+ creates: /var/cache/gerbolyze_container/etc/pacman.d/gnupg
+
+ - name: Fixup pacman.conf for pacman to work in chroot without its own root fs
+ become: yes
+ lineinfile:
+ path: /var/cache/gerbolyze_container/etc/pacman.conf
+ regexp: '^CheckSpace'
+ line: '#CheckSpace'
+
+ - name: Update container and install software
+ become: yes
+ shell: arch-chroot /var/cache/gerbolyze_container pacman -Syu --noconfirm python3 opencv hdf5 gtk3 python-numpy python-pip imagemagick unzip zip
+
+ # TODO maybe install directly from local git checkout?
+ - name: Install gerbolyze
+ become: yes
+ shell: arch-chroot /var/cache/gerbolyze_container pip install -U --upgrade-strategy=eager gerbolyze
+
+ # - name: Cleanup bootstrap image
+ # file:
+ # path: /tmp/arch-bootstrap.tar.xz
+ # state: absent
+
+ - name: Create app cache directory
+ file:
+ path: /var/cache/gerboweb
+ owner: user # FIXME debug
+ group: user # FIXME debug
+ mode: 0770
+
diff --git a/gerboweb/deploy/render.sh b/gerboweb/deploy/render.sh
new file mode 100755
index 0000000..c3920de
--- /dev/null
+++ b/gerboweb/deploy/render.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+[ $# != 1 ] && exit 1
+ID=$1
+egrep -x -q '^[-0-9A-Za-z]{36}$'<<<"$ID" || exit 2
+
+systemd-nspawn \
+ -D /var/cache/gerbolyze_container \
+ -x --bind=/var/cache/gerboweb/upload/$ID:/mnt \
+ /bin/sh -c "set -euo pipefail
+unzip -j -d /tmp/gerber /mnt/gerber.zip
+rm -f /mnt/render_top.png /mnt/render_bottom.png /mnt/render_top.small.png /mnt/render_bottom.small.png
+date; echo 'Rendering bottom layer'
+gerbolyze render top /tmp/gerber /mnt/render_top.png
+date; echo 'Scaling down'
+convert /mnt/render_top.png -resize 500x500 /mnt/render_top.small.png
+date; echo 'Rendering top layer'
+gerbolyze render bottom /tmp/gerber /mnt/render_bottom.png
+date; echo 'Scaling down'
+convert /mnt/render_bottom.png -resize 500x500 /mnt/render_bottom.small.png"
diff --git a/gerboweb/deploy/vector.sh b/gerboweb/deploy/vector.sh
new file mode 100755
index 0000000..5d239d5
--- /dev/null
+++ b/gerboweb/deploy/vector.sh
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+[ $# != 2 ] && exit 1
+ID=$1
+egrep -x -q '^[-0-9A-Za-z]{36}$'<<<"$ID" || exit 2
+LAYER=$2
+egrep -x -q '^(top|bottom)$'<<<"$LAYER" || exit 2
+
+systemd-nspawn \
+ -D /var/cache/gerbolyze_container \
+ -x --bind=/var/cache/gerboweb/upload/$ID:/mnt \
+ /bin/sh -c "set -euo pipefail
+cd /tmp
+unzip -j -d gerber_in /mnt/gerber.zip
+gerbolyze vectorize $LAYER gerber_in gerber /mnt/overlay.png
+rm -f /mnt/gerber_out.zip
+zip -r /mnt/gerber_out.zip gerber"
+
diff --git a/gerboweb/gerboweb.cfg b/gerboweb/gerboweb.cfg
new file mode 100644
index 0000000..02ea211
--- /dev/null
+++ b/gerboweb/gerboweb.cfg
@@ -0,0 +1,4 @@
+MAX_CONTENT_LENGTH=10000000
+SECRET_KEY="FIXME: CHANGE THIS KEY"
+UPLOAD_PATH="/var/cache/gerboweb/upload"
+JOB_QUEUE_DB="/var/cache/gerboweb/job_queue.sqlite3"
diff --git a/gerboweb/gerboweb.py b/gerboweb/gerboweb.py
new file mode 100644
index 0000000..bf2921a
--- /dev/null
+++ b/gerboweb/gerboweb.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3
+
+# TODO create systemd unit file
+# TODO create systemd tmpfiles.d config
+# TODO setup ansible deployment
+# TODO setup webserver user disk quota
+
+import tempfile
+import uuid
+from functools import wraps
+from os import path
+import os
+import sqlite3
+
+from flask import Flask, url_for, redirect, session, make_response, render_template, request, send_file, abort
+from flask_wtf import FlaskForm
+from flask_wtf.file import FileField
+from wtforms.fields import RadioField
+from wtforms.validators import DataRequired
+from werkzeug.utils import secure_filename
+
+from job_queue import JobQueue
+
+app = Flask(__name__, static_url_path='/static')
+app.config.from_envvar('GERBOWEB_SETTINGS')
+
+class UploadForm(FlaskForm):
+ upload_file = FileField(validators=[DataRequired()])
+
+class OverlayForm(UploadForm):
+ upload_file = FileField(validators=[DataRequired()])
+ side = RadioField('Side', choices=[('top', 'Top'), ('bottom', 'Bottom')])
+
+class ResetForm(FlaskForm):
+ pass
+
+job_queue = JobQueue(app.config['JOB_QUEUE_DB'])
+
+def tempfile_path(namespace):
+ """ Return a path for a per-session temporary file identified by the given namespace. Create the session tempfile
+ dir if necessary. The application tempfile dir is controlled via the upload_path config value and not managed by
+ this function. """
+ if not path.isdir(app.config['UPLOAD_PATH']):
+ os.mkdir(app.config['UPLOAD_PATH'])
+ sess_tmp = path.join(app.config['UPLOAD_PATH'], session['session_id'])
+ if not path.isdir(sess_tmp):
+ os.mkdir(sess_tmp)
+
+ return path.join(sess_tmp, namespace)
+
+def require_session_id(fun):
+ @wraps(fun)
+ def wrapper(*args, **kwargs):
+ if 'session_id' not in session:
+ session['session_id'] = str(uuid.uuid4())
+ return fun(*args, **kwargs)
+ return wrapper
+
+@app.route('/')
+@require_session_id
+def index():
+ forms = {
+ 'gerber_form': UploadForm(),
+ 'overlay_form': OverlayForm(),
+ 'reset_form': ResetForm() }
+
+ for job in ('vector_job', 'render_job'):
+ if job in session and job_queue[session[job]].finished:
+ del session[job]
+
+ r = make_response(render_template('index.html',
+ has_renders = path.isfile(tempfile_path('gerber.zip')),
+ has_output = path.isfile(tempfile_path('overlay.png')),
+ **forms))
+ if 'vector_job' in session or 'render_job' in session:
+ r.headers.set('refresh', '10')
+ return r
+
+# NOTES about the gerber and overlay file upload routines
+# * The maximum upload size is limited by the MAX_CONTENT_LENGTH config setting.
+# * The uploaded files are deleted after a while by systemd tmpfiles.d
+# TODO: validate this setting applies *after* gzip transport compression
+
+@app.route('/upload/<namespace>', methods=['POST'])
+@require_session_id
+def upload(namespace):
+ if namespace not in ('gerber', 'overlay'):
+ return abort(400, 'Invalid upload type')
+
+ upload_form = UploadForm() if namespace == 'gerber' else OverlayForm()
+ if upload_form.validate_on_submit():
+ f = upload_form.upload_file.data
+
+ if namespace == 'gerber':
+ f.save(tempfile_path('gerber.zip'))
+ session['filename'] = secure_filename(f.filename) # Cache filename for later download
+ if 'render_job' in session:
+ job_queue.drop(session['render_job'])
+ session['render_job'] = job_queue.enqueue('render',
+ session_id=session['session_id'],
+ client=request.remote_addr)
+ else: # namespace == 'vector'
+ f.save(tempfile_path('overlay.png'))
+
+ # Re-vectorize if either file has changed
+ if path.isfile(tempfile_path('gerber.zip')) and path.isfile(tempfile_path('overlay.png')):
+ if 'vector_job' in session:
+ job_queue.drop(session['vector_job'])
+ session['vector_job'] = job_queue.enqueue('vector',
+ client=request.remote_addr,
+ session_id=session['session_id'],
+ side=upload_form.side.data)
+
+ return redirect(url_for('index'))
+
+@app.route('/render/preview/<side>')
+def render_preview(side):
+ if not side in ('top', 'bottom'):
+ return abort(400, 'side must be either "top" or "bottom"')
+ return send_file(tempfile_path(f'render_{side}.small.png'))
+
+@app.route('/render/download/<side>')
+def render_download(side):
+ if not side in ('top', 'bottom'):
+ return abort(400, 'side must be either "top" or "bottom"')
+ return send_file(tempfile_path(f'render_{side}.png'),
+ mimetype='image/png',
+ as_attachment=True,
+ attachment_filename=f'{path.splitext(session["filename"])[0]}_render.png')
+
+@app.route('/output/download')
+def output_download():
+ return send_file(tempfile_path('gerber_out.zip'),
+ mimetype='application/zip',
+ as_attachment=True,
+ attachment_filename=f'{path.splitext(session["filename"])[0]}_with_artwork.zip')
+
+@app.route('/session_reset', methods=['POST'])
+@require_session_id
+def session_reset():
+ if 'render_job' in session:
+ session['render_job'].abort()
+ if 'vector_job' in session:
+ session['vector_job'].abort()
+ session.clear()
+ return redirect(url_for('index'))
+
diff --git a/gerboweb/job_processor.py b/gerboweb/job_processor.py
new file mode 100644
index 0000000..c138bf4
--- /dev/null
+++ b/gerboweb/job_processor.py
@@ -0,0 +1,40 @@
+
+import signal
+import subprocess
+import logging
+import itertools
+
+from job_queue import JobQueue
+
+
+if __name__ == '__main__':
+ import argparse
+ parser = argparse.ArgumentParser()
+ parser.add_argument('queue', help='job queue sqlite3 database file')
+ parser.add_argument('--loglevel', '-l', default='info')
+ args = parser.parse_args()
+
+ numeric_level = getattr(logging, args.loglevel.upper(), None)
+ if not isinstance(numeric_level, int):
+ raise ValueError('Invalid log level: %s' % loglevel)
+ logging.basicConfig(level=numeric_level)
+
+ job_queue = JobQueue(args.queue)
+
+ signal.signal(signal.SIGALRM, lambda *args: None) # Ignore incoming alarm signals while processing jobs
+ signal.setitimer(signal.ITIMER_REAL, 0.001, 1)
+ while signal.sigwait([signal.SIGALRM, signal.SIGINT]) == signal.SIGALRM:
+ logging.debug('Checking for jobs')
+ for job in job_queue.job_iter('render'):
+ logging.info(f'Processing {job.type} job {job.id} session {job["session_id"]} from {job.client} submitted {job.created}')
+ with job:
+ job.result = subprocess.call(['sudo', '/usr/local/sbin/gerbolyze_render.sh', job['session_id']])
+ logging.info(f'Finishied processing {job.type} job {job.id}')
+
+ for job in job_queue.job_iter('vector'):
+ logging.info(f'Processing {job.type} job {job.id} session {job["session_id"]} from {job.client} submitted {job.created}')
+ with job:
+ job.result = subprocess.call(['sudo', '/usr/local/sbin/gerbolyze_vector.sh', job['session_id'], job['side']])
+ logging.info(f'Finishied processing {job.type} job {job.id}')
+ logging.info('Caught SIGINT. Exiting.')
+
diff --git a/gerboweb/job_queue.py b/gerboweb/job_queue.py
new file mode 100644
index 0000000..e48379d
--- /dev/null
+++ b/gerboweb/job_queue.py
@@ -0,0 +1,88 @@
+
+import json
+import sqlite3
+
+class JobQueue:
+ def __init__(self, dbfile):
+ self.dbfile = dbfile
+ self.db = sqlite3.connect(dbfile, check_same_thread=False)
+ self.db.row_factory = sqlite3.Row
+ with self.db as conn:
+ conn.execute('''CREATE TABLE IF NOT EXISTS jobs
+ (id INTEGER PRIMARY KEY,
+ type TEXT,
+ params TEXT,
+ client TEXT,
+ result TEXT DEFAULT NULL,
+ created DATETIME DEFAULT CURRENT_TIMESTAMP,
+ consumed DATETIME DEFAULT NULL,
+ aborted DATETIME DEFAULT NULL,
+ finished DATETIME DEFAULT NULL);''')
+
+ def enqueue(self, task_type:str, client, **params):
+ """ Enqueue a job of the given type with the given params. Returns the new job ID. """
+ with self.db as conn:
+ return conn.execute('INSERT INTO jobs(type, client, params) VALUES (?, ?, ?)',
+ (task_type, client, json.dumps(params))).lastrowid
+
+ def check_result(slef, job_id):
+ with self.db as conn:
+ job = conn.execute('SELECT * FROM jobs WHERE id=?', (job_id,)).fetchone()
+ if job is None:
+ raise IndexError('Job id not found')
+ return job.result
+
+ def drop(self, job_id):
+ with self.db as conn:
+ return conn.execute('DELETE FROM jobs WHERE id=?', (job_id,)).rowcount > 0
+
+ def pop(self, task_type):
+ """ Fetch the next job of the given type. Returns a sqlite3.Row object of the job or None if no jobs of the given
+ type are queued. """
+ with self.db as conn:
+ job = conn.execute('SELECT * FROM jobs WHERE type=? AND consumed IS NULL ORDER BY created ASC LIMIT 1',
+ (task_type,)).fetchone()
+ if job is None:
+ return None
+
+ # Atomically commit to this job
+ conn.execute('UPDATE jobs SET consumed=datetime("now") WHERE id=?', (job['id'],))
+
+ return Job(self.db, job)
+
+ def job_iter(self, task_type):
+ return iter(lambda: self.pop(task_type), None)
+
+ def __getitem__(self, key):
+ """ Return the job with the given ID, or raise a KeyError if the key cannot be found. """
+ with self.db as conn:
+ job = conn.execute('SELECT * FROM jobs WHERE id=?', (key,)).fetchone()
+ if job is None:
+ raise KeyError(f'Unknown job ID "{key}"')
+
+ return Job(self.db, job)
+
+class Job(dict):
+ def __init__(self, db, row):
+ super().__init__(json.loads(row['params']))
+ self._db = db
+ self._row = row
+ self.id = row['id']
+ self.type = row['type']
+ self.client = row['client']
+ self.created = row['created']
+ self.consumed = row['consumed']
+ self.finished = row['finished']
+ self.result = None
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, _exc_type, _exc_val, _exc_tb):
+ with self._db as conn:
+ conn.execute('UPDATE jobs SET finished=datetime("now"), result=? WHERE id=?', (self.result, self.id,))
+
+ def abort(self):
+ with self._db as conn:
+ conn.execute('UPDATE jobs SET aborted=datetime("now") WHERE id=?', (self.id,))
+
diff --git a/gerboweb/templates/index.html b/gerboweb/templates/index.html
new file mode 100644
index 0000000..c5c8503
--- /dev/null
+++ b/gerboweb/templates/index.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Gerbolyze Raster image to PCB renderer</title>
+ </head>
+ <body>
+ <div class="desc">
+ <h1>Raster image to PCB converter</h1>
+ <p>
+ Gerbolyze is a tool for rendering black and white raster (PNG) images directly onto gerber layers. You can
+ use this to put art on a PCB's silkscreen, solder mask or copper layers. The input is a black-and-white PNG
+ image that is vectorized and rendered into an existing gerber file. Gerbolyze works with gerber files
+ produced with any EDA toolchain and has been tested to work with both Altium and KiCAD.
+ </p>
+ </div>
+
+ <div class="step" id="step1">
+ <h2>Step 1: Upload zipped gerber files</h2>
+ <p>
+ First, upload a zip file containing all your gerber files. The default file names used by KiCAD, Eagle
+ and Altium are supported.
+ </p>
+
+ <form method="POST" action="{{url_for('upload', namespace='gerber')}}" enctype="multipart/form-data">
+ {{gerber_form.csrf_token}}
+ {{gerber_form.upload_file.label}} {{gerber_form.upload_file(size=20)}}
+ <input type="submit" value="Submit">
+ </form>
+ </div>
+
+ {% if 'render_job' in session or has_renders %}
+ <div class="step" id="step2">
+ <h2>Step 2: Download the target side's preview image</h2>
+ <p>
+ Second, download either the top or bottom preview image and use it to align and scale your own artwork
+ in an image editing program such as Gimp. Then upload your overlay image below.
+
+ Note that you will have to convert grayscale images into binary images yourself. Gerbolyze can't do this
+ for you since there are lots of variables involved. Our <a href="{{url_for('static',
+ filename='image_processing_guide.html')}}">Guideline on image processing</a> gives an overview on
+ <i>one</i> way to produce agreeable binary images from grayscale source material.
+ </p>
+ {% if 'render_job' in session %}
+ <strong>Processing...</strong> (this may take several minutes!)
+ {% else %}
+ <img src="{{url_for('render_preview', side='top')}}"> <a href="{{url_for('render_download', side='top')}}">Download</a>
+ <img src="{{url_for('render_preview', side='bottom')}}"> <a href="{{url_for('render_download', side='bottom')}}">Download</a>
+ {% endif %}
+ <form method="POST" action="{{url_for('session_reset')}}">
+ {{reset_form.csrf_token}}
+ <input type="submit" value="Start over">
+ </form>
+ </div>
+
+ <div class="step" id="step3">
+ <h2>Step 3: Upload overlay image</h2>
+ <p>
+ Now, upload your binary overlay image as a PNG and let gerbolyze render it onto the target layer. The PNG
+ file should be a black and white binary file with details generally above about 10px size. <b>Antialiased
+ edges are supported.</b>
+ </p>
+ <form method="POST" action="{{url_for('upload', namespace='overlay')}}" enctype="multipart/form-data">
+ {{overlay_form.csrf_token}}
+ {{overlay_form.upload_file.label}} {{overlay_form.upload_file(size=20)}}
+ {{overlay_form.side.label}} {{overlay_form.side()}}
+ <input type="submit" value="Submit">
+ </form>
+ </div>
+
+ {% if 'vector_job' in session or has_output %}
+ <div class="step" id="step4">
+ <h2> Step 4: Download the processed gerber files</h2>
+ {% if 'vector_job' in session %}
+ <strong>Processing...</strong> (this may take several minutes!)
+ {% else %}
+ <a href="{{url_for('output_download')}}">Download</a>
+ {% endif %}
+ <form method="POST" action="{{url_for('session_reset')}}">
+ {{reset_form.csrf_token}}
+ <input type="submit" value="Start over">
+ </form>
+ </div>
+ {% endif %} {# vector job #}
+ {% endif %} {# render job #}
+ </body>
+</html>