Перейти к содержимому
AlexCatze

Как сделать простой инсталятор на основе github

Рекомендуемые сообщения

Предположим, Вы написали крутую программу. И она размазана, скажем на 10 файлов. Или нет, лучше на 50. И Вам захотелось сделать инсталятор. Первая мысль - вручную заливать все файлы на pastebin, потом писать инсталятор, в нём же вручную прописывать коды файлов на pastebin, и их целевые адреса в системе. Это предложение даже читать больно, не то что совершать описанное в нём действие. Но, выход есть. И так, нам понадобятся две вещи:

  • Github аккаунт
  • Хостинг, на котором можно разместить серверную часть.

И если с первым всё предельно понятно, то со вторым возникает вопрс: А где его, собственно, взять? Вариантов много. Можно арендовать VPS, поднять сервер на домашнем компьютере или Raspberry Pi, можно воспользоваться услугами таких сайтов как Heroku или PythonAnywhere. Последний вариант примечателен тем, что он полностью бесплатный. И так, какой из сайтов выбрать? PythonAnywhere имеет более ограниченный функционал, по сравнению с Heroku. Но, при этом он намного легче в настройке и эксплуатации, так что остановимся на нём. Далее по пунктам:

1)Заливаем программу на гитхаб.

2)Переходим по ссылке, и создаём токен. Ставим только одну галочку:

1866525534_.png.c35ef06fc221428b52faeef70cfb0fc2.png

После создания запоминаем копируем токен, он нам дальше пригодится. Обращаю внимание, что токен нужно держать в секрете.

3) На этом этапе нам нужен хостинг. Я рассмотрю пример с www.pythonanywhere.com . Идём на сайт, регистрируем аккаунт и создаём Flask приложение. В этом нет ничего сложного, если знать английский. Находим файл flask_app.py, и вставляем в него следующее содержимое:

from flask import Flask
from flask import request
import requests
import json

def request_url(url, result):
    r = requests.get(url)
    j = json.loads(r.text)
    for x in j:
        if x["type"] == "file":
            result = result + "\"" + x["path"] + "\","
        else:
            result = request_url(x["url"],result)
    return result

app = Flask(__name__)

#@app.route('/',methods=['POST'])
def main():
    uname = ""# Сюда нужно прописать логин аккаунта Github
    token = ""# А сюда полученный токен
    if uname != request.args["owner"]:
        return "{}"
    result = request_url("https://"+uname+":"+token+"@api.github.com/repos/"+request.args["owner"]+"/"+request.args["repo"]+"/contents"+request.args["path"]+"?ref="+request.args["ref"],"{")
    result = result + "}"
    return result

app.add_url_rule("/", "main", main,methods=['GET'])

Заполняем поля uname и token, и можно двигаться дальше.

4) На своём компьютере создаём Lua файл, и вставляем в него:

local internet = require("internet")
local fs = require("filesystem")
local shell = require("shell")
local ser = require("serialization")

local host = ""--Хост, на котором расположена серверная часть
local owner = ""--Логин аккаунта Github
local repo = ""--Название репозитория
local ref = "master"--Название ветки
local path = "/"--Путь к папке, из которой скачивать файлы
local deploy_to = "/"--Куда скачивать файлы

function request(url)
  local data = ""
  local result, response = pcall(internet.request, url)
  if result then
    local result = pcall(function()
      for chunk in response do
        data = data .. chunk
      end
    end)
  end
  return data  
end

files = ser.unserialize(request("http://"..host.."/?owner="..owner.."&repo="..repo.."&path="..path.."&ref="..ref))

for _,v in pairs(files) do
  local pth = deploy_to..v
  if not fs.exists(pth:match(".*/")) then fs.makeDirectory(pth:match(".*/")) end
  shell.execute("wget -f ".."https://raw.githubusercontent.com/"..owner.."/"..repo.."/"..ref.."/"..v.." "..pth)
end

Заполняем поля, и готово.

Изменено пользователем AlexCatze

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@AlexCatze Есть два предложения:

 

  • Немного упростить гайд, чтобы не заставлять пользователя возвращаться туда-сюда:
11 минуту назад, AlexCatze сказал:

2)Заливаем нашу папку на github

3) Создаём на компьютере файл installer.lua, в него вставляем следующее содержимое:

...

4)Возвращаемся к github`у, открываем любой файл, и нажимаем "raw". Нас интересует адрес файла. Копируем эту часть(без "/" в конце):

174362339_.png.7b687d0330ae51f2ef6d0c2d46982049.png

5)Возвращаемся к нашему файлу, вставляем полученный адрес в поле prefix. Должно получится так:

Пишем проще: заливаем на гитхаб..., копируем адрес..., создаём файл..., вставляем адрес...

 

Страницы разбираются не сложно, по фразе data-pjax="#repo-content-pjax-container".

Это позволит пользователю не повторять процедуру при смене структуры каталогов или добавлении и удалении файлов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
5 минут назад, eu_tomat сказал:

Дописать установщику возможность динамически считывать структуру репозитория

Собственно, с начала я это и хотел сделать, но не разобрался, как получить список всех файлов в репозитории.

8 минут назад, eu_tomat сказал:

Страницы разбираются не сложно, по фразе data-pjax="#repo-content-pjax-container".

Можно, пожалуйста, по подробнее.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
52 минуты назад, AlexCatze сказал:

Можно, пожалуйста, по подробнее.

Вот пример скрипта, считывающую ссылки на элементы каталога:

local url = "https://github.com/AlexCatze/racoon-dev/"

local internet = require("internet")
local html = ""
local result, response = pcall(internet.request, url)
if result then
  local result = pcall(function()
    for chunk in response do
      html = html .. chunk
    end
  end)
end
if result then
  html:gsub( 'data%-pjax="#repo%-content%-pjax%-container" href="(.-)">', function(s)print(s)end )
end

Результатом его выполнения вывод будет следующий вывод:

/AlexCatze/racoon-dev/tree/master/bin
/AlexCatze/racoon-dev/tree/master/boot
/AlexCatze/racoon-dev/tree/master/etc
/AlexCatze/racoon-dev/tree/master/lib
/AlexCatze/racoon-dev/tree/master/www

Далее потребуется рекурсивно обойти все каталоги и найти в них файлы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
8 минут назад, eu_tomat сказал:

Вот пример скрипта, считывающую ссылки на элементы каталога:

Понял, спасибо. Осталось только прикрутить распознавание файлов и каталогов, и будет считывалка. Завтра постараюсь сделать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
1 минуту назад, AlexCatze сказал:

Понял, спасибо. Осталось только прикрутить распознавание файлов и каталогов, и будет считывалка.

Я забыл сразу упомянуть один важный момент. Надо как-то отличать каталоги от файлов. Интуитивно понятное решение: искать в тексте ссылку на raw-формат файла. Но оно потребует дублирования запроса и ожидания отдачи страницы. Есть более быстрое решение: сразу запрашивать сырой формат. Если получишь 404, значит это не файл, а каталог. Это решение потребует меньше трафика и времени на ожидание.

 

Скорее всего, гитхаб имеет API, позволяющий избежать и лишнего трафика и этих трюков с парсингом. Моё решение следует рассматривать как отправную точку, интуитивно понятную, но вряд ли оптимальную.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
20 минут назад, eu_tomat сказал:

Скорее всего, гитхаб имеет API, позволяющий избежать и лишнего трафика и этих трюков с парсингом.

Ну, точно! Есть API:

Скрытый текст

$ curl -i https://api.github.com/repos/AlexCatze/racoon-dev/contents/ 2>/dev/zero | egrep '"(path|url|type)"'
    "path": "bin",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin?ref=master",
    "type": "dir",
    "path": "boot",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/boot?ref=master",
    "type": "dir",
    "path": "etc",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/etc?ref=master",
    "type": "dir",
    "path": "lib",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/lib?ref=master",
    "type": "dir",
    "path": "www",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/www?ref=master",
    "type": "dir",

$ curl -i https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin 2>/dev/zero | egrep '"(path|url|type)"'
    "path": "bin/chat.lua",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin/chat.lua?ref=master",
    "type": "file",
    "path": "bin/chat_server.lua",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin/chat_server.lua?ref=master",
    "type": "file",
    "path": "bin/loader.lua",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin/loader.lua?ref=master",
    "type": "file",
    "path": "bin/mc.lua",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin/mc.lua?ref=master",
    "type": "file",
    "path": "bin/ping.lua",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin/ping.lua?ref=master",
    "type": "file",
    "path": "bin/rnconfig.lua",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin/rnconfig.lua?ref=master",
    "type": "file",
    "path": "bin/routconf.lua",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin/routconf.lua?ref=master",
    "type": "file",
    "path": "bin/router.lua",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin/router.lua?ref=master",
    "type": "file",
    "path": "bin/webserver.lua",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin/webserver.lua?ref=master",
    "type": "file",
    "path": "bin/wr.lua",
    "url": "https://api.github.com/repos/AlexCatze/racoon-dev/contents/bin/wr.lua?ref=master",
    "type": "file",

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
3 минуты назад, eu_tomat сказал:

Ну, точно! Есть API

Огромное спасибо!!! Я как бы знал, что оно есть, но найти в документации нужый метод не получилось. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

А если мне не надо все файлы качать? Зачем мне ридми, тесты, лицензия?

 

В свое время я это сделал проще - качается файл version.lua, в нем текущая версия программы и список файлов, если версии не совпадают, то программа качает файлы и перезапускается. Потом я это немного переосмыслил и сделал в виде бутлоадера.

Бутлоадер представляет собой замену Lua BIOS, с функционалом, позволяющим устанавливать и обновлять программы без операционки. Сам обновляется, обновляет нужные файлы, ставит вейкап сообщения на модем, связанную карту и редстоун.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
11 минуту назад, Doob сказал:

А если мне не надо все файлы качать?

Можно в последней строке между "/contents/" и "?ref=" вписать путь к папке, которую нужно скачать.

Изначально инсталятор делался "под себя". Т.е. у меня была задача одной командой скачивать репозиторий, и этот инсталятор решает её.

18 минут назад, Doob сказал:

В свое время я это сделал проще - качается файл version.lua

Мне оказалось проще сделать такой инсталятор, чем список файлов, который к тому же, нужно будет вручную обновлять.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

@AlexCatze Я столкнулся с неприятным свойством API GitHub:

https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting

Цитата

For unauthenticated requests, the rate limit allows for up to 60 requests per hour. Unauthenticated requests are associated with the originating IP address, and not the user making requests.

Простым языком: для анонимных пользователей доступно не более 60 запросов в час с одного IP-адреса.

 

Из-за этого решение на базе API может оказаться нестабильным. Особенно, учитывая, что на одном IP может работать несколько серверов, и на каждом сервере может играть больше одного игрока.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Тогда есть смысл использовать старый инсталятор. Чуть позже сделаю по нему нормальный гайд.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
14 часа назад, AlexCatze сказал:

Тогда есть смысл использовать старый инсталятор.

Лучше тогда разбирать web-версию гитхаба. Решение, конечно, медленное, но зато не требует обновлять код установщика при изменениях файловой структуры. Можно даже комбинировать вызовы к API и Web в зависимости от исчерпания лимита.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
3 часа назад, eu_tomat сказал:

разбирать

Меня от этого слова уже тошнит. Я пока пару дней отдохну от расчленения гитхаба. Как будет апдейт сети, которую я пишу, к нему сделаю такой инсталятор.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Зацени этот инсталятор.

https://github.com/hohserg1/HoverHelm/blob/master/server/home/hoverhelm/update-or-repair.lua

Он использует github api, чтобы получить список файлов в репозитории

Изменено пользователем hohserg

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
В 24.02.2021 в 21:27, eu_tomat сказал:

Результатом его выполнения вывод будет следующий вывод:

Попробовал на эмуляторе ocelot, получил TLWY. Видимо ответ слишком большой для gsub.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
2 часа назад, AlexCatze сказал:

Попробовал на эмуляторе ocelot, получил TLWY. Видимо ответ слишком большой для gsub.

Проверил у себя на компьютере, gsub в моём примере выполнился за 2.5 сек. А стандартный таймаут для TLWY составляет 5 сек. Запас по времени маловат, поэтому решение неустойчивое, на слабом железе работать не будет.

 

Предлагаю в вызываемую функцию добавить os.sleep(0).

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
7 минут назад, eu_tomat сказал:

Предлагаю в вызываемую функцию добавить os.sleep(0).

Так?

local url = "https://github.com/AlexCatze/racoon-dev/"

local internet = require("internet")
local html = ""
local result, response = pcall(internet.request, url)
if result then
  local result = pcall(function()
    for chunk in response do
      html = html .. chunk
    end
  end)
end
if result then
  html:gsub( 'data%-pjax="#repo%-content%-pjax%-container" href="(.-)">', function(s) os.sleep(0) print(s)end )
end

Только что попробовал, эффект тот же.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
14 минуты назад, AlexCatze сказал:

Так?

...

Только что попробовал, эффект тот же.

Да, верно. А хоть что-нибудь успевает распарситься?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в тему...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


×
×
  • Создать...