Add initial support for Sandstorm (packaging)

sandstorm_support
James Mills 1 year ago
parent 54f946613b
commit cac89f130e
Signed by: prologic
GPG Key ID: AC4C014F1440EBD6

1
.gitignore vendored

@ -11,6 +11,7 @@ yarnd_linux
cross_build.sh
environ.inc
/app
/dist
/yarnc

@ -0,0 +1,5 @@
# vagrant-spk creates shell scripts, which must end in \n, even on a \r\n system.
*.sh text eol=lf

@ -0,0 +1,8 @@
# This file stores a list of sub-paths of .sandstorm/ that should be ignored by git.
.vagrant
# Vagrant sometimes generates log files:
*.log

@ -0,0 +1,110 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
# CAUTION: DO NOT MAKE CHANGES TO THIS FILE. The vagrant-spk upgradevm process will overwrite it.
# Guess at a reasonable name for the VM based on the folder vagrant-spk is
# run from. The timestamp is there to avoid conflicts if you have multiple
# folders with the same name.
VM_NAME = File.basename(File.dirname(File.dirname(__FILE__))) + "_sandstorm_#{Time.now.utc.to_i}"
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
# ugly hack to prevent hashicorp's bitrot. See https://github.com/hashicorp/vagrant/issues/9442
# this setting is required for pre-2.0 vagrant, but causes an error as of 2.0.3,
# remove entirely when confident nobody uses vagrant 1.x for anything.
unless Vagrant::DEFAULT_SERVER_URL.frozen?
Vagrant::DEFAULT_SERVER_URL.replace('https://vagrantcloud.com')
end
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# Base on a 64-bit Debian box with vboxsf support (ex. contrib-buster64, bullseye64)
config.vm.box = "debian/contrib-buster64"
config.vm.post_up_message = "Your virtual server is running at: http://local.sandstorm.io:6090"
if Vagrant.has_plugin?("vagrant-vbguest") then
# vagrant-vbguest is a Vagrant plugin that upgrades
# the version of VirtualBox Guest Additions within each
# guest. If you have the vagrant-vbguest plugin, then it
# needs to know how to compile kernel modules, etc., and so
# we give it this hint about operating system type.
config.vm.guest = "debian"
end
# We forward port 6090, the vagrant-spk web port, so that developers can
# visit their Sandstorm app from their browser as local.sandstorm.io:6090
# (aka 127.0.0.1:6090).
config.vm.network :forwarded_port, guest: 6090, host: 6090, host_ip: "127.0.0.1"
# Use a shell script to "provision" the box. This installs Sandstorm using
# the bundled installer.
config.vm.provision "shell", inline: "sudo bash /opt/app/.sandstorm/global-setup.sh", keep_color: true
# Then, do stack-specific and app-specific setup.
config.vm.provision "shell", inline: "sudo bash /opt/app/.sandstorm/setup.sh", keep_color: true
# Shared folders are configured per-provider since vboxsf can't handle >4096 open files,
# NFS requires privilege escalation every time you bring a VM up,
# and 9p is only available on libvirt.
# Calculate the number of CPUs and the amount of RAM the system has,
# in a platform-dependent way; further logic below.
cpus = nil
total_kB_ram = nil
host = RbConfig::CONFIG['host_os']
if host =~ /darwin/
cpus = `sysctl -n hw.ncpu`.to_i
total_kB_ram = `sysctl -n hw.memsize`.to_i / 1024
elsif host =~ /linux/
cpus = `nproc`.to_i
total_kB_ram = `grep MemTotal /proc/meminfo | awk '{print $2}'`.to_i
elsif host =~ /mingw/
cpus = `powershell -Command "(Get-WmiObject Win32_Processor -Property NumberOfLogicalProcessors | Select-Object -Property NumberOfLogicalProcessors | Measure-Object NumberOfLogicalProcessors -Sum).Sum"`.to_i
total_kB_ram = `powershell -Command "[math]::Round((Get-WmiObject -Class Win32_ComputerSystem).TotalPhysicalMemory)"`.to_i / 1024
end
# Use the same number of CPUs within Vagrant as the system, with 1
# as a default.
#
# Use at least 512MB of RAM, and if the system has more than 2GB of
# RAM, use 1/4 of the system RAM. This seems a reasonable compromise
# between having the Vagrant guest operating system not run out of
# RAM entirely (which it basically would if we went much lower than
# 512MB) and also allowing it to use up a healthily large amount of
# RAM so it can run faster on systems that can afford it.
if cpus.nil? or cpus.zero?
cpus = 1
end
if total_kB_ram.nil? or total_kB_ram < 2048000
assign_ram_mb = 512
else
assign_ram_mb = (total_kB_ram / 1024 / 4)
end
# Actually apply these CPU/memory values to the providers.
config.vm.provider :virtualbox do |vb, override|
vb.cpus = cpus
vb.memory = assign_ram_mb
vb.name = VM_NAME
vb.customize ["modifyvm", :id, "--nictype1", "Am79C973"]
# /opt/app and /host-dot-sandstorm are used by vagrant-spk
override.vm.synced_folder "..", "/opt/app"
override.vm.synced_folder ENV["HOME"] + "/.sandstorm", "/host-dot-sandstorm"
# /vagrant is not used by vagrant-spk; we need this line so it gets disabled; if we removed the
# line, vagrant would automatically insert a synced folder in /vagrant, which is not what we want.
override.vm.synced_folder "..", "/vagrant", disabled: true
end
config.vm.provider :libvirt do |libvirt, override|
libvirt.cpus = cpus
libvirt.memory = assign_ram_mb
libvirt.default_prefix = VM_NAME
# /opt/app and /host-dot-sandstorm are used by vagrant-spk
override.vm.synced_folder "..", "/opt/app", type: "9p", accessmode: "passthrough"
override.vm.synced_folder ENV["HOME"] + "/.sandstorm", "/host-dot-sandstorm", type: "9p", accessmode: "passthrough"
# /vagrant is not used by vagrant-spk; we need this line so it gets disabled; if we removed the
# line, vagrant would automatically insert a synced folder in /vagrant, which is not what we want.
override.vm.synced_folder "..", "/vagrant", type: "9p", accessmode: "passthrough", disabled: true
end
end

@ -0,0 +1,22 @@
#!/bin/bash
set -euo pipefail
cd /opt/app
if [ ! -e go.mod ]; then
printf "Error: This directory does not contain a go module;\n"
printf "vagrant-spk's golang stack does not support older GOPATH\n"
printf "based projects. Try running:\n" >&2
printf "\n" >&2
printf " vagrant-spk vm ssh\n" >&2
printf " cd /opt/app\n" >&2
printf " go mod init example.com/mypkg\n" >&2
exit 1
fi
PATH="$HOME/go/bin:$PATH"
make deps
make preflight server
mv yarnd app
exit 0

@ -0,0 +1,59 @@
#!/bin/bash
set -euo pipefail
# CAUTION: DO NOT MAKE CHANGES TO THIS FILE. The vagrant-spk upgradevm process will overwrite it.
# App-specific setup should be done in the setup.sh file.
# Set options for curl. Since we only want to show errors from these curl commands, we also use
# 'cat' to buffer the output; for more information:
# https://github.com/sandstorm-io/vagrant-spk/issues/158
CURL_OPTS="--silent --show-error"
echo localhost > /etc/hostname
hostname localhost
# Grub updates don't silent install well
apt-mark hold grub-pc
apt-get update
apt-get upgrade -y
# Install curl that is needed below.
apt-get install -y curl
# The following line copies stderr through stderr to cat without accidentally leaving it in the
# output file. Be careful when changing. See: https://github.com/sandstorm-io/vagrant-spk/pull/159
curl $CURL_OPTS https://install.sandstorm.io/ 2>&1 > /host-dot-sandstorm/caches/install.sh | cat
SANDSTORM_CURRENT_VERSION=$(curl $CURL_OPTS -f "https://install.sandstorm.io/dev?from=0&type=install")
SANDSTORM_PACKAGE="sandstorm-$SANDSTORM_CURRENT_VERSION.tar.xz"
if [[ ! -f /host-dot-sandstorm/caches/$SANDSTORM_PACKAGE ]] ; then
echo -n "Downloading Sandstorm version ${SANDSTORM_CURRENT_VERSION}..."
curl $CURL_OPTS --output "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE.partial" "https://dl.sandstorm.io/$SANDSTORM_PACKAGE" 2>&1 | cat
mv "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE.partial" "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE"
echo "...done."
fi
if [ ! -e /opt/sandstorm/latest/sandstorm ] ; then
echo -n "Installing Sandstorm version ${SANDSTORM_CURRENT_VERSION}..."
bash /host-dot-sandstorm/caches/install.sh -d -e -p 6090 "/host-dot-sandstorm/caches/$SANDSTORM_PACKAGE" >/dev/null
echo "...done."
fi
modprobe ip_tables
# Make the vagrant user part of the sandstorm group so that commands like
# `spk dev` work.
usermod -a -G 'sandstorm' 'vagrant'
# Bind to all addresses, so the vagrant port-forward works.
sudo sed --in-place='' \
--expression='s/^BIND_IP=.*/BIND_IP=0.0.0.0/' \
/opt/sandstorm/sandstorm.conf
# Force vagrant-spk to use the strict CSP, see sandstorm#3424 for details.
echo 'ALLOW_LEGACY_RELAXED_CSP=false' >> /opt/sandstorm/sandstorm.conf
sudo service sandstorm restart
# Enable apt-cacher-ng proxy to make things faster if one appears to be running on the gateway IP
GATEWAY_IP=$(ip route | grep ^default | cut -d ' ' -f 3)
if nc -z "$GATEWAY_IP" 3142 ; then
echo "Acquire::http::Proxy \"http://$GATEWAY_IP:3142\";" > /etc/apt/apt.conf.d/80httpproxy
fi
# Configure apt to retry fetching things that fail to download.
echo "APT::Acquire::Retries \"10\";" > /etc/apt/apt.conf.d/80sandstorm-retry

@ -0,0 +1 @@
exec /opt/app/app

@ -0,0 +1,34 @@
# *** WARNING: GENERATED FILE ***
# This file is automatically updated and rewritten in sorted order every time
# the app runs in dev mode. You may manually add or remove files, but don't
# expect comments or ordering to be retained.
bin
etc/ld.so.cache
etc/localtime
lib
lib64
opt/app/.sandstorm/launcher.sh
opt/app/app
proc/cpuinfo
sandstorm-http-bridge
sandstorm-http-bridge-config
sandstorm-manifest
usr/bin/bash
usr/lib/x86_64-linux-gnu/ld-2.28.so
usr/lib/x86_64-linux-gnu/libc-2.28.so
usr/lib/x86_64-linux-gnu/libc.so.6
usr/lib/x86_64-linux-gnu/libdl-2.28.so
usr/lib/x86_64-linux-gnu/libdl.so.2
usr/lib/x86_64-linux-gnu/libnsl-2.28.so
usr/lib/x86_64-linux-gnu/libnsl.so.1
usr/lib/x86_64-linux-gnu/libnss_compat-2.28.so
usr/lib/x86_64-linux-gnu/libnss_compat.so.2
usr/lib/x86_64-linux-gnu/libnss_files-2.28.so
usr/lib/x86_64-linux-gnu/libnss_files.so.2
usr/lib/x86_64-linux-gnu/libnss_nis-2.28.so
usr/lib/x86_64-linux-gnu/libnss_nis.so.2
usr/lib/x86_64-linux-gnu/libtinfo.so.6
usr/lib/x86_64-linux-gnu/libtinfo.so.6.1
usr/lib64/ld-linux-x86-64.so.2
usr/share/zoneinfo/Etc/UTC
usr/share/zoneinfo/UCT

@ -0,0 +1,252 @@
@0xbe9dcaec3e4a5212;
using Spk = import "/sandstorm/package.capnp";
# This imports:
# $SANDSTORM_HOME/latest/usr/include/sandstorm/package.capnp
# Check out that file to see the full, documented package definition format.
const pkgdef :Spk.PackageDefinition = (
# The package definition. Note that the spk tool looks specifically for the
# "pkgdef" constant.
id = "2wt7y0gya6zdj852v7yc5jnqs7y49z8yqy140tc8t72yqydt0gxh",
# Your app ID is actually its public key. The private key was placed in
# your keyring. All updates must be signed with the same key.
manifest = (
# This manifest is included in your app package to tell Sandstorm
# about your app.
appTitle = (defaultText = "Yarn.social"),
appVersion = 0, # Increment this for every release.
appMarketingVersion = (defaultText = "0.9.0"),
# Human-readable representation of appVersion. Should match the way you
# identify versions of your app in documentation and marketing.
actions = [
# Define your "new document" handlers here.
( nounPhrase = (defaultText = "pod"),
command = .myCommand
# The command to run when starting for the first time. (".myCommand"
# is just a constant defined at the bottom of the file.)
)
],
continueCommand = .myCommand,
# This is the command called to start your app back up after it has been
# shut down for inactivity. Here we're using the same command as for
# starting a new instance, but you could use different commands for each
# case.
metadata = (
# Data which is not needed specifically to execute the app, but is useful
# for purposes like marketing and display. These fields are documented at
# https://docs.sandstorm.io/en/latest/developing/publishing-apps/#add-required-metadata
# and (in deeper detail) in the sandstorm source code, in the Metadata section of
# https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/package.capnp
icons = (
# Various icons to represent the app in various contexts.
appGrid = (svg = embed "../assets/logo.svg"),
grain = (svg = embed "../assets/logo.svg"),
market = (svg = embed "../assets/logo.svg"),
marketBig = (svg = embed "../assets/logo.svg"),
),
website = "https://yarn.social",
# This should be the app's main website url.
codeUrl = "https://git.mills.io/yarnsocial/yarn",
# URL of the app's source code repository, e.g. a GitHub URL.
# Required if you specify a license requiring redistributing code, but optional otherwise.
license = (none = void),
# The license this package is distributed under. See
# https://docs.sandstorm.io/en/latest/developing/publishing-apps/#license
categories = [],
# A list of categories/genres to which this app belongs, sorted with best fit first.
# See the list of categories at
# https://docs.sandstorm.io/en/latest/developing/publishing-apps/#categories
author = (
# Fields relating to the author of this app.
contactEmail = "support@yarn.social",
# Email address to contact for any issues with this app. This includes end-user support
# requests as well as app store administrator requests, so it is very important that this be a
# valid address with someone paying attention to it.
#pgpSignature = embed "path/to/pgp-signature",
# PGP signature attesting responsibility for the app ID. This is a binary-format detached
# signature of the following ASCII message (not including the quotes, no newlines, and
# replacing <app-id> with the standard base-32 text format of the app's ID):
#
# "I am the author of the Sandstorm.io app with the following ID: <app-id>"
#
# You can create a signature file using `gpg` like so:
#
# echo -n "I am the author of the Sandstorm.io app with the following ID: <app-id>" | gpg --sign > pgp-signature
#
# Further details including how to set up GPG and how to use keybase.io can be found
# at https://docs.sandstorm.io/en/latest/developing/publishing-apps/#verify-your-identity
upstreamAuthor = "Yarn.social Team",
# Name of the original primary author of this app, if it is different from the person who
# produced the Sandstorm package. Setting this implies that the author connected to the PGP
# signature only "packaged" the app for Sandstorm, rather than developing the app.
# Remove this line if you consider yourself as the author of the app.
),
#pgpKeyring = embed "path/to/pgp-keyring",
# A keyring in GPG keyring format containing all public keys needed to verify PGP signatures in
# this manifest (as of this writing, there is only one: `author.pgpSignature`).
#
# To generate a keyring containing just your public key, do:
#
# gpg --export <key-id> > keyring
#
# Where `<key-id>` is a PGP key ID or email address associated with the key.
#description = (defaultText = embed "path/to/description.md"),
# The app's description in Github-flavored Markdown format, to be displayed e.g.
# in an app store. Note that the Markdown is not permitted to contain HTML nor image tags (but
# you can include a list of screenshots separately).
shortDescription = (defaultText = "better social media"),
# A very short (one-to-three words) description of what the app does. For example,
# "Document editor", or "Notetaking", or "Email client". This will be displayed under the app
# title in the grid view in the app market.
screenshots = [
# Screenshots to use for marketing purposes. Examples below.
# Sizes are given in device-independent pixels, so if you took these
# screenshots on a Retina-style high DPI screen, divide each dimension by two.
#(width = 746, height = 795, jpeg = embed "path/to/screenshot-1.jpeg"),
#(width = 640, height = 480, png = embed "path/to/screenshot-2.png"),
],
changeLog = (defaultText = embed "../CHANGELOG.md"),
# Documents the history of changes in Github-flavored markdown format (with the same restrictions
# as govern `description`). We recommend formatting this with an H1 heading for each version
# followed by a bullet list of changes.
),
),
sourceMap = (
# Here we defined where to look for files to copy into your package. The
# `spk dev` command actually figures out what files your app needs
# automatically by running it on a FUSE filesystem. So, the mappings
# here are only to tell it where to find files that the app wants.
searchPath = [
( sourcePath = "." ), # Search this directory first.
( sourcePath = "/", # Then search the system root directory.
hidePaths = [ "home", "proc", "sys",
"etc/passwd", "etc/hosts", "etc/host.conf",
"etc/nsswitch.conf", "etc/resolv.conf" ]
# You probably don't want the app pulling files from these places,
# so we hide them. Note that /dev, /var, and /tmp are implicitly
# hidden because Sandstorm itself provides them.
)
]
),
fileList = "sandstorm-files.list",
# `spk dev` will write a list of all the files your app uses to this file.
# You should review it later, before shipping your app.
alwaysInclude = [],
# Fill this list with more names of files or directories that should be
# included in your package, even if not listed in sandstorm-files.list.
# Use this to force-include stuff that you know you need but which may
# not have been detected as a dependency during `spk dev`. If you list
# a directory here, its entire contents will be included recursively.
#bridgeConfig = (
# # Used for integrating permissions and roles into the Sandstorm shell
# # and for sandstorm-http-bridge to pass to your app.
# # Uncomment this block and adjust the permissions and roles to make
# # sense for your app.
# # For more information, see high-level documentation at
# # https://docs.sandstorm.io/en/latest/developing/auth/
# # and advanced details in the "BridgeConfig" section of
# # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/package.capnp
# viewInfo = (
# # For details on the viewInfo field, consult "ViewInfo" in
# # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/grain.capnp
#
# permissions = [
# # Permissions which a user may or may not possess. A user's current
# # permissions are passed to the app as a comma-separated list of `name`
# # fields in the X-Sandstorm-Permissions header with each request.
# #
# # IMPORTANT: only ever append to this list! Reordering or removing fields
# # will change behavior and permissions for existing grains! To deprecate a
# # permission, or for more information, see "PermissionDef" in
# # https://github.com/sandstorm-io/sandstorm/blob/master/src/sandstorm/grain.capnp
# (
# name = "editor",
# # Name of the permission, used as an identifier for the permission in cases where string
# # names are preferred. Used in sandstorm-http-bridge's X-Sandstorm-Permissions HTTP header.
#
# title = (defaultText = "editor"),
# # Display name of the permission, e.g. to display in a checklist of permissions
# # that may be assigned when sharing.
#
# description = (defaultText = "grants ability to modify data"),
# # Prose describing what this role means, suitable for a tool tip or similar help text.
# ),
# ],
# roles = [
# # Roles are logical collections of permissions. For instance, your app may have
# # a "viewer" role and an "editor" role
# (
# title = (defaultText = "editor"),
# # Name of the role. Shown in the Sandstorm UI to indicate which users have which roles.
#
# permissions = [true],
# # An array indicating which permissions this role carries.
# # It should be the same length as the permissions array in
# # viewInfo, and the order of the lists must match.
#
# verbPhrase = (defaultText = "can make changes to the document"),
# # Brief explanatory text to show in the sharing UI indicating
# # what a user assigned this role will be able to do with the grain.
#
# description = (defaultText = "editors may view all site data and change settings."),
# # Prose describing what this role means, suitable for a tool tip or similar help text.
# ),
# (
# title = (defaultText = "viewer"),
# permissions = [false],
# verbPhrase = (defaultText = "can view the document"),
# description = (defaultText = "viewers may view what other users have written."),
# ),
# ],
# ),
# #apiPath = "/api",
# # Apps can export an API to the world. The API is to be used primarily by Javascript
# # code and native apps, so it can't serve out regular HTML to browsers. If a request
# # comes in to your app's API, sandstorm-http-bridge will prefix the request's path with
# # this string, if specified.
#),
);
const myCommand :Spk.Manifest.Command = (
# Here we define the command used to start up your server.
argv = ["/sandstorm-http-bridge", "8000", "--", "/bin/bash", "/opt/app/.sandstorm/launcher.sh"],
environ = [
# Note that this defines the *entire* environment seen by your app.
(key = "PATH", value = "/usr/local/bin:/usr/bin:/bin"),
(key = "SANDSTORM", value = "1"),
(key = "DEBUG", value = "true"),
(key = "DATA", value = "/var/data"),
(key = "STORE", value = "bitcask:///var/data/yarn.db"),
(key = "OPEN_REGISTRATIONS", value = "true"),
(key = "OPEN_PROFILES", value = "true"),
# Export SANDSTORM=1 into the environment, so that apps running within Sandstorm
# can detect if $SANDSTORM="1" at runtime, switching UI and/or backend to use
# the app's Sandstorm-specific integration code.
]
);

@ -0,0 +1,22 @@
#!/bin/bash
# When you change this file, you must take manual action. Read this doc:
# - https://docs.sandstorm.io/en/latest/vagrant-spk/customizing/#setupsh
set -euo pipefail
version=1.17.4
os=linux
arch=amd64
# The version of golang in the debian repositories tends to be incredibly
# out of date; let's get ourselves a newer version from upstream:
curl -L https://golang.org/dl/go${version}.${os}-${arch}.tar.gz -o go.tar.gz
tar -C /usr/local -xzf go.tar.gz
rm go.tar.gz
echo 'export PATH=/usr/local/go/bin:$PATH' > /etc/profile.d/go.sh
# Needed for fetching go libraries:
apt-get install -y git
exit 0

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 487.002 487.002" style="enable-background:new 0 0 487.002 487.002;" xml:space="preserve">
<path d="M416.854,74.149C371.618,28.914,311.475,4.001,247.502,4.001S123.386,28.914,78.15,74.149
C35.216,117.083,10.606,173.45,8.212,233.771c-0.227,0.96-0.263,1.953-0.107,2.924c-0.063,2.264-0.103,4.532-0.103,6.806
c0,63.973,24.913,124.116,70.148,169.352c12.405,12.405,25.938,23.271,40.356,32.524c-8.769-3.357-16.844-6.776-24.497-10.02
C63.562,422.455,39.513,412.264,5.784,420.2c-4.032,0.949-6.532,4.986-5.583,9.019c0.949,4.033,4.988,6.531,9.019,5.583
c28.955-6.813,49.92,2.071,78.936,14.367c35.569,15.073,79.835,33.832,159.346,33.832c63.973,0,124.116-24.913,169.352-70.148
c45.235-45.236,70.148-105.379,70.148-169.352S462.089,119.385,416.854,74.149z M421.065,385.926L228.892,467.23
c-13.553-1.105-26.86-3.414-39.797-6.875l254.722-107.767C437.296,364.284,429.701,375.442,421.065,385.926z M393.928,413.695
c-35.626,30.746-79.424,49.25-126.214,53.398L393.928,413.695z M453.855,332.19c-0.429,0.097-0.856,0.226-1.276,0.404
L167.277,453.299c-9.202-3.515-18.159-7.646-26.829-12.362l323.303-136.782C461.082,313.727,457.77,323.086,453.855,332.19z
M467.98,286.078L124.52,431.388c-7.03-4.622-13.828-9.657-20.362-15.098l367.174-155.343
C470.684,269.43,469.558,277.815,467.98,286.078z M54.01,357.506l410.063-173.488c1.945,7.125,3.537,14.363,4.772,21.694
L66.255,376.039C61.852,370.045,57.769,363.861,54.01,357.506z M37.922,324.313l212.463-89.888c0.024-0.01,0.047-0.02,0.071-0.03
l200.972-85.027c3.057,6.612,5.785,13.37,8.176,20.253L46.768,344.283C43.491,337.772,40.54,331.109,37.922,324.313z
M39.681,158.286l104.7,104.7l-25.579,10.822c-0.305-1.322-0.965-2.58-1.996-3.61L30.867,184.26
C33.271,175.414,36.214,166.742,39.681,158.286z M58.943,121.549l124.788,124.788l-24.445,10.342L46.295,143.688
C50.073,136.091,54.296,128.703,58.943,121.549z M105.029,279.634l-24.445,10.342l-57.322-57.322
c0.498-10.531,1.73-20.927,3.651-31.136L105.029,279.634z M23.243,253.849l42.434,42.434l-32.67,13.822
C27.419,292.029,24.114,273.161,23.243,253.849z M94.157,79.549c8.163-7.647,16.8-14.618,25.845-20.886v46.731L94.157,79.549z
M135.002,49.131c8.078-4.694,16.425-8.865,25-12.493v108.757l-25-25V49.131z M175.002,30.924c8.171-2.779,16.515-5.084,25-6.903
v161.373l-25-25V30.924z M215.002,21.33c8.233-1.187,16.577-1.917,25-2.191V222.53l-2.013,0.851l-22.987-22.987V21.33z
M400.002,154.838l-25,10.577V58.664c8.734,6.052,17.087,12.761,25,20.1V154.838z M360.002,171.761l-25,10.577v-145.7
c8.575,3.628,16.922,7.8,25,12.493V171.761z M320.002,188.684l-25,10.577V24.022c8.485,1.819,16.829,4.124,25,6.903V188.684z
M280.002,205.607l-25,10.577V19.138c8.423,0.275,16.767,1.005,25,2.191V205.607z M444.661,135.944l-29.659,12.548V94.029
C426.571,106.961,436.491,121.018,444.661,135.944z M83.55,90.156l139.533,139.533l-24.445,10.342L67.668,109.06
C72.582,102.515,77.882,96.206,83.55,90.156z M75.894,388.248l395.013-167.121c0.726,7.388,1.094,14.853,1.094,22.374
c0,0.294-0.01,0.586-0.011,0.88L91.81,405.227c-1.023-0.986-2.046-1.973-3.054-2.98C84.247,397.737,79.967,393.061,75.894,388.248z"
/>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

Loading…
Cancel
Save