716 lines
No EOL
20 KiB
Go
716 lines
No EOL
20 KiB
Go
package main
|
||
|
||
import (
|
||
"archive/zip"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"io/ioutil"
|
||
"os"
|
||
"os/exec"
|
||
"github.com/BurntSushi/toml"
|
||
"time"
|
||
"log"
|
||
"net/http"
|
||
"strings"
|
||
"bytes"
|
||
"encoding/xml"
|
||
"github.com/avast/apkparser"
|
||
"strconv"
|
||
"crypto/sha256"
|
||
"encoding/hex"
|
||
"path/filepath"
|
||
"net/url"
|
||
"path"
|
||
"flag"
|
||
)
|
||
|
||
type Repo struct {
|
||
Timestamp int64 `json:"timestamp"`
|
||
Name string `json:"name"`
|
||
Version int64 `json:"version"`
|
||
Icon string `json:"icon"`
|
||
Address string `json:"address"`
|
||
Description string `json:"description"`
|
||
}
|
||
|
||
type Requests struct {
|
||
Install []string `json:"install"`
|
||
Uninstall []string `json:"uninstall"`
|
||
}
|
||
|
||
type Localized struct {
|
||
ENUS Icon `json:"en-US"`
|
||
}
|
||
|
||
type Icon struct {
|
||
Icon string `json:"icon"`
|
||
}
|
||
|
||
type App struct {
|
||
AuthorName string `json:"authorName"`
|
||
Categories []string `json:"categories"`
|
||
SuggestedVersionCode string `json:"suggestedVersionCode"`
|
||
IssueTracker string `json:"issueTracker"`
|
||
License string `json:"license"`
|
||
Name string `json:"name"`
|
||
SourceCode string `json:"sourceCode"`
|
||
Summary string `json:"summary"`
|
||
WebSite string `json:"webSite"`
|
||
Added int64 `json:"added"`
|
||
PackageName string `json:"packageName"`
|
||
LastUpdated int64 `json:"lastUpdated"`
|
||
Localized Localized `json:"localized,omitempty"`
|
||
}
|
||
|
||
type Index struct {
|
||
Repo Repo `json:"repo"`
|
||
Requests Requests `json:"requests"`
|
||
Apps []App `json:"apps"`
|
||
Packages map[string][]Package `json:"packages"`
|
||
}
|
||
|
||
type Config struct {
|
||
Name string `toml:"name"`
|
||
Icon string `toml:"icon"`
|
||
Address string `toml:"address"`
|
||
Description string `toml:"description"`
|
||
KeyStorePath string `toml:"keystore_path"`
|
||
KeyStorePassword string `toml:"keystore_password"`
|
||
KeyStoreAlias string `toml:"keystore_alias"`
|
||
BinarySources []string `toml:"binarySources"`
|
||
GithubToken string `toml:"github_token"`
|
||
RelicConfig string `toml:"relic_config"`
|
||
}
|
||
|
||
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"`
|
||
PreRelease bool `json:"prerelease"`
|
||
}
|
||
|
||
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 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")
|
||
if apkExists(iconPath) {
|
||
return iconPath
|
||
}
|
||
// 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 contains(slice []string, item string) bool {
|
||
for _, s := range slice {
|
||
if s == item {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
func processAPK(filePath string,indexCache Index) Package {
|
||
fmt.Printf("Processing APK: %s\n", filePath)
|
||
for _, pkgs := range indexCache.Packages {
|
||
for _, pkg := range pkgs {
|
||
if pkg.ApkName == filePath {
|
||
log.Printf("Warning: using %s metadata from cache",filePath)
|
||
for _, app := range indexCache.Apps {
|
||
if app.PackageName == pkg.PackageName {
|
||
pkg.ApplicationName = app.Name
|
||
break
|
||
}
|
||
}
|
||
return pkg
|
||
}
|
||
}
|
||
}
|
||
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)
|
||
}
|
||
|
||
var directories []string
|
||
|
||
// Iterate through the files in the ZIP archive
|
||
for _, file := range zipReader.File {
|
||
// Check if the file is a directory and starts with 'lib/'
|
||
if file.IsDir {
|
||
// Check if the directory is directly under 'lib/'
|
||
if strings.HasPrefix(file.Name, "lib/") && strings.Count(file.Name, "/") == 1 {
|
||
// Extract the directory name without the 'lib/' prefix
|
||
dirName := strings.TrimPrefix(file.Name, "lib/")
|
||
directories = append(directories, strings.TrimSuffix(dirName, "/"))
|
||
}
|
||
} else {
|
||
// If it's a file, check if it belongs to a directory under 'lib/'
|
||
if strings.HasPrefix(file.Name, "lib/") {
|
||
// Extract the directory name
|
||
dirName := strings.Split(file.Name, "/")[1] // Get the first subdirectory under 'lib'
|
||
if !contains(directories, dirName) {
|
||
directories = append(directories, dirName)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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(),
|
||
NativeCode: directories,
|
||
}
|
||
|
||
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,indexCache Index) []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(filePath) {
|
||
fmt.Printf("APK already exists: %s\n", filePath)
|
||
}else{
|
||
err = downloadAPK(asset.BrowserDownloadURL, filePath)
|
||
if err != nil {
|
||
fmt.Printf("Error downloading APK: %v\n", err)
|
||
os.Exit(1)
|
||
}
|
||
}
|
||
packageInfo := processAPK(filePath,indexCache)
|
||
packages = append(packages,packageInfo)
|
||
}
|
||
|
||
return packages
|
||
}
|
||
func getReleases(repo string,config Config) ([]Release, error) {
|
||
url := fmt.Sprintf("https://api.github.com/repos/%s/releases", repo)
|
||
req, err := http.NewRequest("GET",url,nil)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if config.GithubToken != "" {
|
||
req.Header.Set("Authorization", "token "+config.GithubToken)
|
||
}
|
||
client := http.Client{}
|
||
resp,err := client.Do(req)
|
||
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,config Config) (Repository, error) {
|
||
fmt.Println("Fetching repository info")
|
||
url := fmt.Sprintf("https://api.github.com/repos/%s", repo)
|
||
req, err := http.NewRequest("GET",url,nil)
|
||
if err != nil {
|
||
return Repository{}, err
|
||
}
|
||
if config.GithubToken != "" {
|
||
req.Header.Set("Authorization", "token "+config.GithubToken)
|
||
}
|
||
client := &http.Client{}
|
||
resp,err := client.Do(req)
|
||
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 {
|
||
data, err := json.MarshalIndent(index, "", " ")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return ioutil.WriteFile(filename, data, 0644)
|
||
}
|
||
|
||
func createJar(zipFileName string, files []string) error {
|
||
zipFile, err := os.Create(zipFileName)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer zipFile.Close()
|
||
|
||
zipWriter := zip.NewWriter(zipFile)
|
||
defer zipWriter.Close()
|
||
|
||
for _, file := range files {
|
||
err := addFileToZip(zipWriter, file)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func addFileToZip(zipWriter *zip.Writer, file string) error {
|
||
fileToZip, err := os.Open(file)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer fileToZip.Close()
|
||
|
||
w, err := zipWriter.Create(file)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
_, err = io.Copy(w, fileToZip)
|
||
return err
|
||
}
|
||
|
||
func signJarWithJarsigner(jarFile string, keystore string, password string, alias string) (error, string) {
|
||
cmd := exec.Command("jarsigner", "-sigalg","SHA1withRSA","-digestalg","SHA1","-keystore", keystore, "-storetype", "PKCS12", "-storepass", password, jarFile, alias)
|
||
output, err := cmd.CombinedOutput() // Сначала получаем вывод и ошибку
|
||
return err, string(output) // Возвращаем ошибку и вывод как строку
|
||
}
|
||
|
||
func signJarWithRelic(jarFile string, keystore string, password string, alias string,relicConfig string) (error, string) {
|
||
cmd := exec.Command("relic", "sign","--digest","SHA1","--key-alias",alias,"--file",jarFile,"--output",jarFile,"--key",keystore,"--config",relicConfig)
|
||
//cmd.Env = append(cmd.Env, "KEYSTORE_PASSWORD="+password)
|
||
output, err := cmd.CombinedOutput() // Сначала получаем вывод и ошибку
|
||
return err, string(output) // Возвращаем ошибку и вывод как строку
|
||
}
|
||
|
||
func createAppFromReleases(releases []Release, repo string, index int,indexCache Index,config Config) (App, []Package,error) {
|
||
// Получаем информацию о репозитории
|
||
repository, err := getRepositoryInfo(repo,config)
|
||
if err != nil {
|
||
return App{}, []Package{}, err
|
||
}
|
||
|
||
// Предположим, что мы берем данные из первого релиза
|
||
if len(releases) == 0 {
|
||
return App{}, []Package{}, nil
|
||
}
|
||
release := releases[index]
|
||
packages := handleRelease(release,indexCache)
|
||
if len(packages) == 0 {
|
||
return createAppFromReleases(releases,repo,index+1,indexCache,config)
|
||
//return App{}, []Package{}, nil
|
||
}
|
||
//fmt.Printf("Debug: ",repository.License)
|
||
if repository.License == nil {
|
||
repository.License = &License {
|
||
SPDXID: "",
|
||
}
|
||
}
|
||
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() {
|
||
var apps []App
|
||
var indexCache Index
|
||
var packages map[string][]Package
|
||
packages = make(map[string][]Package)
|
||
var config Config
|
||
|
||
configPath := flag.String("config", "config.toml", "Path to the configuration file")
|
||
useRelic := flag.Bool("relic",false,"Use relic instead that jarsigner")
|
||
// Parse the command-line flags
|
||
flag.Parse()
|
||
file, err := os.Open(*configPath)
|
||
fileJson,errJson := os.Open("index-v1.json")
|
||
if err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
if errJson != nil {
|
||
log.Printf("Error opening index cache so cache will not used: %s",errJson)
|
||
indexCache = Index{}
|
||
}
|
||
defer file.Close()
|
||
defer fileJson.Close()
|
||
// Parse the TOML file into the config struct
|
||
if errJson := json.NewDecoder(fileJson).Decode(&indexCache); err != nil {
|
||
log.Printf("Error decoding index cache so cache will not used: %s",errJson)
|
||
}
|
||
if _, err := toml.NewDecoder(file).Decode(&config); err != nil {
|
||
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,config)
|
||
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,0,indexCache,config)
|
||
if err != nil {
|
||
log.Fatal("Failed to create app from releases: ",err)
|
||
}
|
||
if len(app_packages) != 0 {
|
||
apps = append(apps,app)
|
||
packages[app_packages[0].PackageName] = append(packages[app_packages[0].PackageName], app_packages...)
|
||
}
|
||
}
|
||
|
||
index := Index {
|
||
Repo: Repo{
|
||
Timestamp: time.Now().Unix(),
|
||
Name: config.Name,
|
||
Icon: "icon.png",
|
||
Version: 1002,
|
||
Address: config.Address,
|
||
Description: config.Description,
|
||
},
|
||
Requests: Requests{
|
||
Install: []string{},
|
||
Uninstall: []string{},
|
||
},
|
||
Apps: apps,
|
||
Packages: packages,
|
||
}
|
||
// Генерация JSON файла
|
||
err = generateJSON(index, "index-v1.json")
|
||
if err != nil {
|
||
fmt.Println("Error generating JSON:", err)
|
||
return
|
||
}
|
||
fmt.Println("JSON file generated successfully.")
|
||
|
||
// Создание JAR файла
|
||
err = createJar("index-v1.jar", []string{"index-v1.json"})
|
||
if err != nil {
|
||
fmt.Println("Error creating JAR:", err)
|
||
return
|
||
}
|
||
fmt.Println("JAR file created successfully.")
|
||
|
||
// Подпись JAR файла
|
||
p12File := config.KeyStorePath // Укажите путь к вашему P12 файлу
|
||
password := config.KeyStorePassword // Укажите пароль для P12 файла
|
||
alias := config.KeyStoreAlias // Укажите алиас для ключа
|
||
if *useRelic == true {
|
||
err, output := signJarWithRelic("index-v1.jar", p12File, password, alias,config.RelicConfig)
|
||
if err != nil {
|
||
fmt.Println("Error signing JAR:", err, output)
|
||
return
|
||
}
|
||
fmt.Println("JAR file signed successfully.")
|
||
}else{
|
||
err, output := signJarWithJarsigner("index-v1.jar", p12File, password, alias)
|
||
if err != nil {
|
||
fmt.Println("Error signing JAR:", err, output)
|
||
return
|
||
}
|
||
fmt.Println("JAR file signed successfully.")
|
||
|
||
}
|
||
} |