Now repository can have apps and allowed to install them
This commit is contained in:
parent
d004e69e6b
commit
71c1312df9
4 changed files with 512 additions and 7 deletions
28
apktest.go
Normal file
28
apktest.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
|
"github.com/avast/apkparser"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
enc := xml.NewEncoder(os.Stdout)
|
||||||
|
//enc.Indent("", "\t")
|
||||||
|
zipErr, resErr, manErr := apkparser.ParseApk(os.Args[1], enc)
|
||||||
|
if zipErr != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to open the APK: %s", zipErr.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if resErr != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to parse resources: %s", resErr.Error())
|
||||||
|
}
|
||||||
|
if manErr != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to parse AndroidManifest.xml: %s", manErr.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
5
go.mod
5
go.mod
|
@ -3,3 +3,8 @@ module fdroidgo
|
||||||
go 1.24.2
|
go 1.24.2
|
||||||
|
|
||||||
require github.com/BurntSushi/toml v1.5.0
|
require github.com/BurntSushi/toml v1.5.0
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/avast/apkparser v0.0.0-20250423072857-abc1843ceb56 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
|
)
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -1,2 +1,6 @@
|
||||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
github.com/avast/apkparser v0.0.0-20250423072857-abc1843ceb56 h1:gFlKwiEFHiSUH79HIhSa2jJmM6gZg4xQaZIkMjUS73c=
|
||||||
|
github.com/avast/apkparser v0.0.0-20250423072857-abc1843ceb56/go.mod h1:3F9A8btIerUcuy7Fmno+g/nIk4ELKJ6NCs2/KK1bvLs=
|
||||||
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
|
|
482
main.go
482
main.go
|
@ -8,9 +8,20 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"github.com/BurntSushi/toml"
|
"github.com/BurntSushi/toml"
|
||||||
"time"
|
"time"
|
||||||
"log"
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"bytes"
|
||||||
|
"encoding/xml"
|
||||||
|
"github.com/avast/apkparser"
|
||||||
|
"strconv"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
|
"path/filepath"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Repo struct {
|
type Repo struct {
|
||||||
|
@ -55,6 +66,7 @@ type Index struct {
|
||||||
Repo Repo `json:"repo"`
|
Repo Repo `json:"repo"`
|
||||||
Requests Requests `json:"requests"`
|
Requests Requests `json:"requests"`
|
||||||
Apps []App `json:"apps"`
|
Apps []App `json:"apps"`
|
||||||
|
Packages map[string][]Package `json:"packages"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
@ -62,6 +74,389 @@ type Config struct {
|
||||||
Icon string `toml:"icon"`
|
Icon string `toml:"icon"`
|
||||||
Address string `toml:"address"`
|
Address string `toml:"address"`
|
||||||
Description string `toml:"description"`
|
Description string `toml:"description"`
|
||||||
|
KeyStorePath string `toml:"keystore_path"`
|
||||||
|
KeyStorePassword string `toml:"keystore_password"`
|
||||||
|
KeyStoreAlias string `toml:"keystore_alias"`
|
||||||
|
BinarySources []string `toml:"binarySources"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
Login string `json:"login"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ReleaseAsset struct {
|
||||||
|
BrowserDownloadURL string `json:"browser_download_url"`
|
||||||
|
ID int `json:"id"`
|
||||||
|
Size int `json:"size"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
Uploader *User `json:"uploader,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type Release struct {
|
||||||
|
TagName string `json:"tag_name"`
|
||||||
|
Name *string `json:"name,omitempty"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
PublishedAt *time.Time `json:"published_at,omitempty"`
|
||||||
|
Author User `json:"author"`
|
||||||
|
Assets []ReleaseAsset `json:"assets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Repository struct {
|
||||||
|
License *License `json:"license"`
|
||||||
|
HTMLURL string `json:"html_url"`
|
||||||
|
IssuesUrl string `json:"issues_url"`
|
||||||
|
Homepage string `json:"homepage"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type License struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
SPDXID string `json:"spdx_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Package struct {
|
||||||
|
Added int64 `json:"added"`
|
||||||
|
ApkName string `json:"apkName"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
HashType string `json:"hashType"`
|
||||||
|
MinSdkVersion int `json:"minSdkVersion"`
|
||||||
|
NativeCode []string `json:"nativecode"`
|
||||||
|
PackageName string `json:"packageName"`
|
||||||
|
Sig string `json:"sig"`
|
||||||
|
Signer string `json:"signer"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
TargetSdkVersion int `json:"targetSdkVersion"`
|
||||||
|
UsesPermission [][]*string `json:"uses-permission"`
|
||||||
|
UsesPermissionSdk23 [][]string `json:"uses-permission-sdk-23"`
|
||||||
|
VersionCode int64 `json:"versionCode"`
|
||||||
|
VersionName string `json:"versionName"`
|
||||||
|
ApplicationName string `json:"-"`
|
||||||
|
}
|
||||||
|
type Application struct {
|
||||||
|
Label string `xml:"label,attr"`
|
||||||
|
Icon string `xml:"icon,attr"`
|
||||||
|
// Add other application attributes as needed
|
||||||
|
}
|
||||||
|
type Manifest struct {
|
||||||
|
PackageName string `xml:"package,attr"`
|
||||||
|
VersionCode int64 `xml:"versionCode,attr"`
|
||||||
|
VersionName string `xml:"versionName,attr"`
|
||||||
|
UsesSdk UsesSdk `xml:"uses-sdk"`
|
||||||
|
UsesPermission []UsesPermission `xml:"uses-permission"`
|
||||||
|
Application Application `xml:"application"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structure for <uses-sdk>
|
||||||
|
|
||||||
|
type UsesSdk struct {
|
||||||
|
MinSdkVersion string `xml:"minSdkVersion,attr"`
|
||||||
|
TargetSdkVersion string `xml:"targetSdkVersion,attr"`
|
||||||
|
}
|
||||||
|
// Structure for <uses-permission>
|
||||||
|
type UsesPermission struct {
|
||||||
|
Name string `xml:"name,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Structure for <uses-feature>
|
||||||
|
|
||||||
|
|
||||||
|
func apkExists(filePath string) bool {
|
||||||
|
_, err := os.Stat(filePath)
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to check if the URL is an APK
|
||||||
|
func isAPK(url string) bool {
|
||||||
|
return strings.HasSuffix(url, ".apk")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Function to download the APK file
|
||||||
|
func downloadAPK(url string, filePath string) error {
|
||||||
|
fmt.Printf("Downloading %s",url)
|
||||||
|
response, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
if response.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("failed to download file: %s", response.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(file, response.Body)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func getResourceTypeId(resourceType string) int {
|
||||||
|
switch resourceType {
|
||||||
|
case "string":
|
||||||
|
return 0
|
||||||
|
case "drawable":
|
||||||
|
return 1
|
||||||
|
case "layout":
|
||||||
|
return 2
|
||||||
|
case "mipmap":
|
||||||
|
return 3
|
||||||
|
case "color":
|
||||||
|
return 4
|
||||||
|
case "id":
|
||||||
|
return 5
|
||||||
|
case "style":
|
||||||
|
return 6
|
||||||
|
case "anim":
|
||||||
|
return 7
|
||||||
|
case "menu":
|
||||||
|
return 8
|
||||||
|
case "xml":
|
||||||
|
return 9
|
||||||
|
case "raw":
|
||||||
|
return 10
|
||||||
|
case "font":
|
||||||
|
return 11
|
||||||
|
case "transition":
|
||||||
|
return 12
|
||||||
|
case "drawable-v21":
|
||||||
|
return 13
|
||||||
|
default:
|
||||||
|
return -1 // Invalid resource type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func saveIconFile(f *apkparser.ZipReaderFile, packageName string) string {
|
||||||
|
// Create the directory if it does not exist
|
||||||
|
err := os.MkdirAll(packageName + "/en-US", os.ModePerm)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error creating directory:", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define the path for the icon file
|
||||||
|
|
||||||
|
iconPath := filepath.Join(packageName, "en-US")
|
||||||
|
iconPath = filepath.Join(iconPath,"icon.png")
|
||||||
|
// Create a new file to save the icon
|
||||||
|
outFile, err := os.Create(iconPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error creating icon file:", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer outFile.Close()
|
||||||
|
|
||||||
|
// Open the zip file for reading
|
||||||
|
err = f.Open()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error opening zip file:", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Copy the contents of the zip file to the new file
|
||||||
|
if _, err := io.Copy(outFile, f); err != nil {
|
||||||
|
fmt.Println("Error copying icon file:", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return iconPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func processAPK(filePath string) Package {
|
||||||
|
fmt.Printf("Processing APK: %s\n", filePath)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
enc := xml.NewEncoder(&buf)
|
||||||
|
|
||||||
|
zipFile, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error opening ZIP file:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
return Package{}
|
||||||
|
}
|
||||||
|
defer zipFile.Close()
|
||||||
|
|
||||||
|
// Get the file info
|
||||||
|
fileInfo, err := zipFile.Stat()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error getting file info:", err)
|
||||||
|
os.Exit(1)
|
||||||
|
return Package{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a zip.Reader
|
||||||
|
zipReader, zipErr := apkparser.OpenZipReader(zipFile)
|
||||||
|
p,resErr := apkparser.NewParser(zipReader,enc)
|
||||||
|
|
||||||
|
manErr := p.ParseXml("AndroidManifest.xml")
|
||||||
|
if zipErr != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to open the APK: %s\n", zipErr.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resErr != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to parse resources: %s\n", resErr.Error())
|
||||||
|
}
|
||||||
|
if manErr != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to parse AndroidManifest.xml: %s\n", manErr.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestXML := buf.String()
|
||||||
|
var manifest Manifest
|
||||||
|
if err := xml.Unmarshal([]byte(manifestXML), &manifest); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to unmarshal manifest XML: %s\n", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
minsdk, err := strconv.Atoi(manifest.UsesSdk.MinSdkVersion)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Can't parse minsdk: ", manifest.UsesSdk.MinSdkVersion)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
targetsdk, err := strconv.Atoi(manifest.UsesSdk.TargetSdkVersion)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Can't parse targetsdk: ", manifest.UsesSdk.TargetSdkVersion)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate SHA-256 hash of the APK file
|
||||||
|
hash, err := calculateSHA256(filePath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Failed to calculate SHA-256 hash: %s\n", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
packageInfo := Package{
|
||||||
|
ApkName: filePath,
|
||||||
|
VersionCode: manifest.VersionCode,
|
||||||
|
VersionName: manifest.VersionName,
|
||||||
|
MinSdkVersion: minsdk,
|
||||||
|
TargetSdkVersion: targetsdk,
|
||||||
|
PackageName: manifest.PackageName,
|
||||||
|
UsesPermission: make([][]*string, len(manifest.UsesPermission)),
|
||||||
|
ApplicationName: manifest.Application.Label,
|
||||||
|
Hash: hash,
|
||||||
|
HashType: "SHA-256",
|
||||||
|
Size: fileInfo.Size(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, perm := range manifest.UsesPermission {
|
||||||
|
packageInfo.UsesPermission[i] = append(packageInfo.UsesPermission[i], &perm.Name)
|
||||||
|
packageInfo.UsesPermission[i] = append(packageInfo.UsesPermission[i], nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(manifest.Application.Icon, "@") {
|
||||||
|
iconFile := zipReader.File[manifest.Application.Icon]
|
||||||
|
saveIconFile(iconFile,manifest.PackageName)
|
||||||
|
}
|
||||||
|
return packageInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculateSHA256 computes the SHA-256 hash of the given file.
|
||||||
|
func calculateSHA256(filePath string) (string, error) {
|
||||||
|
fmt.Println("Calculating SHA256")
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
hasher := sha256.New()
|
||||||
|
if _, err := io.Copy(hasher, file); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := hasher.Sum(nil)
|
||||||
|
return hex.EncodeToString(hash), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFilenameFromURL(urlStr string) (string, error) {
|
||||||
|
// Parse the URL
|
||||||
|
parsedURL, err := url.Parse(urlStr)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the path from the URL and extract the filename
|
||||||
|
filename := path.Base(parsedURL.Path)
|
||||||
|
return filename, nil
|
||||||
|
}
|
||||||
|
// Function to handle downloading and processing APKs from a release
|
||||||
|
func handleRelease(release Release) []Package {
|
||||||
|
var packages []Package
|
||||||
|
for _, asset := range release.Assets {
|
||||||
|
if !isAPK(asset.BrowserDownloadURL) {
|
||||||
|
//fmt.Printf("Skipping non-APK file: %s\n", asset.BrowserDownloadURL)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
filePath,err := getFilenameFromURL(asset.BrowserDownloadURL)
|
||||||
|
if apkExists(asset.BrowserDownloadURL) {
|
||||||
|
fmt.Printf("APK already exists: %s\n", asset.BrowserDownloadURL)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
err = downloadAPK(asset.BrowserDownloadURL, filePath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error downloading APK: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
packageInfo := processAPK(filePath)
|
||||||
|
packages = append(packages,packageInfo)
|
||||||
|
}
|
||||||
|
return packages
|
||||||
|
}
|
||||||
|
func getReleases(repo string) ([]Release, error) {
|
||||||
|
url := fmt.Sprintf("https://api.github.com/repos/%s/releases", repo)
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("failed to get releases: %s", resp.Status)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
var releases []Release
|
||||||
|
// Декодируем JSON-ответ в структуру Release
|
||||||
|
// Декодируем JSON-ответ в pструктуру Release
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&releases); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return releases, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func getRepositoryInfo(repo string) (Repository, error) {
|
||||||
|
fmt.Println("Fetching repository info")
|
||||||
|
url := fmt.Sprintf("https://api.github.com/repos/%s", repo)
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return Repository{}, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return Repository{}, fmt.Errorf("failed to get repository info: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
var repository Repository
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&repository); err != nil {
|
||||||
|
return Repository{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return repository, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateJSON(index Index, filename string) error {
|
func generateJSON(index Index, filename string) error {
|
||||||
|
@ -114,7 +509,48 @@ func signJarWithJarsigner(jarFile string, keystore string, password string, alia
|
||||||
return err, string(output) // Возвращаем ошибку и вывод как строку
|
return err, string(output) // Возвращаем ошибку и вывод как строку
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createAppFromReleases(releases []Release, repo string) (App, []Package,error) {
|
||||||
|
// Получаем информацию о репозитории
|
||||||
|
repository, err := getRepositoryInfo(repo)
|
||||||
|
if err != nil {
|
||||||
|
return App{}, []Package{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Предположим, что мы берем данные из первого релиза
|
||||||
|
if len(releases) == 0 {
|
||||||
|
return App{}, []Package{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
release := releases[0]
|
||||||
|
packages := handleRelease(release)
|
||||||
|
|
||||||
|
app := App{
|
||||||
|
AuthorName: release.Author.Login,
|
||||||
|
Categories: []string{"fdroid"},
|
||||||
|
SuggestedVersionCode: release.TagName,
|
||||||
|
IssueTracker: repository.HTMLURL + "/issues",
|
||||||
|
License: repository.License.SPDXID,
|
||||||
|
Name: packages[0].ApplicationName,
|
||||||
|
SourceCode: repository.HTMLURL,
|
||||||
|
Summary: repository.Description,
|
||||||
|
WebSite: repository.Homepage,
|
||||||
|
Added: time.Now().Unix(),
|
||||||
|
PackageName: packages[0].PackageName,
|
||||||
|
LastUpdated: release.CreatedAt.Unix(),
|
||||||
|
Localized: Localized{
|
||||||
|
ENUS: Icon {
|
||||||
|
Icon: "icon.png",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return app, packages, nil
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var apps []App
|
||||||
|
var packages map[string][]Package
|
||||||
|
packages = make(map[string][]Package)
|
||||||
var config Config
|
var config Config
|
||||||
file, err := os.Open("config.toml")
|
file, err := os.Open("config.toml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -126,6 +562,37 @@ func main() {
|
||||||
if _, err := toml.NewDecoder(file).Decode(&config); err != nil {
|
if _, err := toml.NewDecoder(file).Decode(&config); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, source := range config.BinarySources {
|
||||||
|
fmt.Printf("Processing %s\n",source)
|
||||||
|
// Извлекаем имя репозитория из ссылки
|
||||||
|
repo := strings.TrimPrefix(source, "github.com/")
|
||||||
|
if repo == source { // Если не удалось извлечь, пропускаем
|
||||||
|
fmt.Printf("Invalid repository format: %s\n", source)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получаем релизы для репозитория
|
||||||
|
fmt.Println("Fetching releases")
|
||||||
|
releases, err := getReleases(repo)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error fetching releases for %s: %v\n", repo, err)
|
||||||
|
os.Exit(1)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var app App
|
||||||
|
fmt.Println("Creating app object from repository info and apk")
|
||||||
|
app,app_packages, err := createAppFromReleases(releases,repo)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to create app from releases: ",err)
|
||||||
|
}
|
||||||
|
apps = append(apps,app)
|
||||||
|
packages[app_packages[0].PackageName] = append(packages[app_packages[0].PackageName], app_packages...)
|
||||||
|
}
|
||||||
|
|
||||||
index := Index {
|
index := Index {
|
||||||
Repo: Repo{
|
Repo: Repo{
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
|
@ -139,7 +606,8 @@ func main() {
|
||||||
Install: []string{},
|
Install: []string{},
|
||||||
Uninstall: []string{},
|
Uninstall: []string{},
|
||||||
},
|
},
|
||||||
Apps: []App{},
|
Apps: apps,
|
||||||
|
Packages: packages,
|
||||||
}
|
}
|
||||||
// Генерация JSON файла
|
// Генерация JSON файла
|
||||||
err = generateJSON(index, "index-v1.json")
|
err = generateJSON(index, "index-v1.json")
|
||||||
|
@ -158,9 +626,9 @@ func main() {
|
||||||
fmt.Println("JAR file created successfully.")
|
fmt.Println("JAR file created successfully.")
|
||||||
|
|
||||||
// Подпись JAR файла
|
// Подпись JAR файла
|
||||||
p12File := "../../keystore.p12" // Укажите путь к вашему P12 файлу
|
p12File := config.KeyStorePath // Укажите путь к вашему P12 файлу
|
||||||
password := "u86viXB0FRlTY2K1buzGqWHdDu6pIgu8520R3IJgXAE=" // Укажите пароль для P12 файла
|
password := config.KeyStorePassword // Укажите пароль для P12 файла
|
||||||
alias := "curious-invention.aeza.network" // Укажите алиас для ключа
|
alias := config.KeyStoreAlias // Укажите алиас для ключа
|
||||||
|
|
||||||
err, output := signJarWithJarsigner("index-v1.jar", p12File, password, alias)
|
err, output := signJarWithJarsigner("index-v1.jar", p12File, password, alias)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue