separate file handling functions from main file
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
4e09ac34f2
commit
375e468a8e
117
main.go
117
main.go
@ -1,15 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,9 +28,9 @@ type HTTPResponseJSON struct {
|
|||||||
|
|
||||||
// Caches data about available ROMs in memory so we don't need to reference the filesystem for each request
|
// Caches data about available ROMs in memory so we don't need to reference the filesystem for each request
|
||||||
type ROMCache struct {
|
type ROMCache struct {
|
||||||
ROMs []LineageOSROM `json:"roms"`
|
ROMs []LineageOSROM `json:"roms"`
|
||||||
Cached map[string]bool `json:"-"` // to quickly lookup if a file is already cached
|
Cached map[string]bool `json:"-"` // to quickly lookup if a file is already cached
|
||||||
sync.Mutex `json:"-"` // We have multiple goroutines that may be accessing this data simultaneously, so we much lock / unlock it to prevent race conditions
|
sync.RWMutex `json:"-"` // We have multiple goroutines that may be accessing this data simultaneously, so we much lock / unlock it to prevent race conditions
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -161,9 +158,9 @@ func updateROMCache() {
|
|||||||
|
|
||||||
// save file to disk for next startup so we don't have to rehash all the files again
|
// save file to disk for next startup so we don't have to rehash all the files again
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
romCache.Lock()
|
romCache.RLock()
|
||||||
romCacheJson, err := json.Marshal(romCache)
|
romCacheJson, err := json.Marshal(romCache)
|
||||||
romCache.Unlock()
|
romCache.RUnlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("failed to marshal romCache to json: %v", err)
|
log.Printf("failed to marshal romCache to json: %v", err)
|
||||||
return
|
return
|
||||||
@ -181,58 +178,6 @@ func updateROMCache() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if new builds were moved
|
|
||||||
func moveBuildArtifacts() bool {
|
|
||||||
|
|
||||||
f, err := os.Open(buildOutDirectory)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("failed to open ROM directory: %v", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
files, err := f.ReadDir(0)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("failed to read files in directory: %v", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var newROMs bool
|
|
||||||
|
|
||||||
for _, v := range files {
|
|
||||||
if isLineageROM, _ := isLineageROMZip(v); !isLineageROM { // skip files that aren't LineageOS ROMs
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
newROMs = true
|
|
||||||
log.Printf("new build found - moving file %s", v.Name())
|
|
||||||
romCache.Lock() // lock to prevent multiple concurrent goroutines moving the same file
|
|
||||||
err := moveBuildFile(fmt.Sprintf("%s/%s", buildOutDirectory, v.Name()), fmt.Sprintf("%s/%s", romDirectory, v.Name()))
|
|
||||||
romCache.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("failed to move file '%s' from out to rom directory: %v", v.Name(), err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return newROMs
|
|
||||||
}
|
|
||||||
|
|
||||||
// false if a file is not a LineageOS ROM .zip file
|
|
||||||
// no formal validation is performed - only file naming convention is checked
|
|
||||||
// also returns a lineage ROM's filename sliced and delimited by -'s
|
|
||||||
// Example filename: lineage-20.0-20230604-UNOFFICIAL-sunfish.zip
|
|
||||||
func isLineageROMZip(v fs.DirEntry) (bool, []string) {
|
|
||||||
|
|
||||||
// skip directories, non .zip files and files that don't begin with lineage-
|
|
||||||
if v.Type().IsDir() || !strings.HasSuffix(v.Name(), ".zip") || !strings.HasPrefix(v.Name(), "lineage-") {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
splitName := strings.Split(v.Name(), "-")
|
|
||||||
// expect 5 dashes
|
|
||||||
return len(splitName) == 5, splitName
|
|
||||||
}
|
|
||||||
|
|
||||||
// http - GET /
|
// http - GET /
|
||||||
// Writes JSON response for the updater app to know what versions are available to download
|
// Writes JSON response for the updater app to know what versions are available to download
|
||||||
func lineageOSROMListHandler(w http.ResponseWriter, r *http.Request) {
|
func lineageOSROMListHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -256,55 +201,3 @@ func lineageOSROMListHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
w.Write(b)
|
w.Write(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a sha256 hash of a file located at the path provided
|
|
||||||
func hashFile(filename string) (string, error) {
|
|
||||||
f, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to open file '%s': %v: ", filename, err)
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
h := sha256.New()
|
|
||||||
if _, err := io.Copy(h, f); err != nil {
|
|
||||||
return "", fmt.Errorf("failed to copy data from file to hash function: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// A custom "move file" function because in docker container the mounted folders are different overlay filesystems
|
|
||||||
// Instead of os.Rename, we must copy and delete
|
|
||||||
func moveBuildFile(src, dst string) error {
|
|
||||||
sourceFileStat, err := os.Stat(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !sourceFileStat.Mode().IsRegular() {
|
|
||||||
return fmt.Errorf("%s is not a regular file", src)
|
|
||||||
}
|
|
||||||
|
|
||||||
source, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer source.Close()
|
|
||||||
|
|
||||||
destination, err := os.Create(dst)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer destination.Close()
|
|
||||||
_, err = io.Copy(destination, source)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to copy file: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.Remove(src)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to delete source file after copy: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
117
processFiles.go
Normal file
117
processFiles.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/fs"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Searches the build toolchain output directory for new LineageOS builds.
|
||||||
|
// Any new builds are moved into the public http server directory.
|
||||||
|
// Returns true if new builds were moved
|
||||||
|
func moveBuildArtifacts() bool {
|
||||||
|
|
||||||
|
f, err := os.Open(buildOutDirectory)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to open ROM directory: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
files, err := f.ReadDir(0)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to read files in directory: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
var newROMs bool
|
||||||
|
|
||||||
|
for _, v := range files {
|
||||||
|
if isLineageROM, _ := isLineageROMZip(v); !isLineageROM { // skip files that aren't LineageOS ROMs
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
newROMs = true
|
||||||
|
log.Printf("new build found - moving file %s", v.Name())
|
||||||
|
romCache.Lock() // lock to prevent multiple concurrent goroutines moving the same file
|
||||||
|
err := moveBuildFile(fmt.Sprintf("%s/%s", buildOutDirectory, v.Name()), fmt.Sprintf("%s/%s", romDirectory, v.Name()))
|
||||||
|
romCache.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to move file '%s' from out to rom directory: %v", v.Name(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newROMs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a sha256 hash of a file located at the path provided
|
||||||
|
func hashFile(filename string) (string, error) {
|
||||||
|
f, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to open file '%s': %v: ", filename, err)
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
h := sha256.New()
|
||||||
|
if _, err := io.Copy(h, f); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to copy data from file to hash function: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// false if a file is not a LineageOS ROM .zip file
|
||||||
|
// no formal validation is performed - only file naming convention is checked
|
||||||
|
// also returns a lineage ROM's filename sliced and delimited by -'s
|
||||||
|
// Example filename: lineage-20.0-20230604-UNOFFICIAL-sunfish.zip
|
||||||
|
func isLineageROMZip(v fs.DirEntry) (bool, []string) {
|
||||||
|
|
||||||
|
// skip directories, non .zip files and files that don't begin with lineage-
|
||||||
|
if v.Type().IsDir() || !strings.HasSuffix(v.Name(), ".zip") || !strings.HasPrefix(v.Name(), "lineage-") {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
splitName := strings.Split(v.Name(), "-")
|
||||||
|
// expect 5 dashes
|
||||||
|
return len(splitName) == 5, splitName
|
||||||
|
}
|
||||||
|
|
||||||
|
// A custom "move file" function because in docker container the mounted folders are different overlay filesystems
|
||||||
|
// Instead of os.Rename, we must copy and delete
|
||||||
|
func moveBuildFile(src, dst string) error {
|
||||||
|
sourceFileStat, err := os.Stat(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !sourceFileStat.Mode().IsRegular() {
|
||||||
|
return fmt.Errorf("%s is not a regular file", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
source, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer source.Close()
|
||||||
|
|
||||||
|
destination, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer destination.Close()
|
||||||
|
_, err = io.Copy(destination, source)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to copy file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Remove(src)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to delete source file after copy: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user