mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
Build UI artifacts (#4824)
* Flag/env var for stash UI location * Include UI in build artifacts
This commit is contained in:
parent
237a904ca4
commit
408d6fc988
6 changed files with 90 additions and 29 deletions
15
.github/workflows/build.yml
vendored
15
.github/workflows/build.yml
vendored
|
|
@ -92,13 +92,16 @@ jobs:
|
||||||
docker exec -t build /bin/bash -c "make build-cc-linux-arm32v6"
|
docker exec -t build /bin/bash -c "make build-cc-linux-arm32v6"
|
||||||
docker exec -t build /bin/bash -c "make build-cc-freebsd"
|
docker exec -t build /bin/bash -c "make build-cc-freebsd"
|
||||||
|
|
||||||
|
- name: Zip UI
|
||||||
|
run: docker exec -t build /bin/bash -c "make zip-ui"
|
||||||
|
|
||||||
- name: Cleanup build container
|
- name: Cleanup build container
|
||||||
run: docker rm -f -v build
|
run: docker rm -f -v build
|
||||||
|
|
||||||
- name: Generate checksums
|
- name: Generate checksums
|
||||||
run: |
|
run: |
|
||||||
git describe --tags --exclude latest_develop | tee CHECKSUMS_SHA1
|
git describe --tags --exclude latest_develop | tee CHECKSUMS_SHA1
|
||||||
sha1sum dist/Stash.app.zip dist/stash-* | sed 's/dist\///g' | tee -a CHECKSUMS_SHA1
|
sha1sum dist/Stash.app.zip dist/stash-* dist/stash-ui.zip | sed 's/dist\///g' | tee -a CHECKSUMS_SHA1
|
||||||
echo "STASH_VERSION=$(git describe --tags --exclude latest_develop)" >> $GITHUB_ENV
|
echo "STASH_VERSION=$(git describe --tags --exclude latest_develop)" >> $GITHUB_ENV
|
||||||
echo "RELEASE_DATE=$(date +'%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_ENV
|
echo "RELEASE_DATE=$(date +'%Y-%m-%d %H:%M:%S %Z')" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
|
@ -126,6 +129,14 @@ jobs:
|
||||||
name: stash-linux
|
name: stash-linux
|
||||||
path: dist/stash-linux
|
path: dist/stash-linux
|
||||||
|
|
||||||
|
- name: Upload UI
|
||||||
|
# only upload for pull requests
|
||||||
|
if: ${{ github.event_name == 'pull_request' && github.base_ref != 'refs/heads/develop' && github.base_ref != 'refs/heads/master'}}
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: stash-ui.zip
|
||||||
|
path: dist/stash-ui.zip
|
||||||
|
|
||||||
- name: Update latest_develop tag
|
- name: Update latest_develop tag
|
||||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
|
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/develop' }}
|
||||||
run : git tag -f latest_develop; git push -f --tags
|
run : git tag -f latest_develop; git push -f --tags
|
||||||
|
|
@ -147,6 +158,7 @@ jobs:
|
||||||
dist/stash-linux-arm32v7
|
dist/stash-linux-arm32v7
|
||||||
dist/stash-linux-arm32v6
|
dist/stash-linux-arm32v6
|
||||||
dist/stash-freebsd
|
dist/stash-freebsd
|
||||||
|
dist/stash-ui.zip
|
||||||
CHECKSUMS_SHA1
|
CHECKSUMS_SHA1
|
||||||
|
|
||||||
- name: Master release
|
- name: Master release
|
||||||
|
|
@ -166,6 +178,7 @@ jobs:
|
||||||
dist/stash-linux-arm32v7
|
dist/stash-linux-arm32v7
|
||||||
dist/stash-linux-arm32v6
|
dist/stash-linux-arm32v6
|
||||||
dist/stash-freebsd
|
dist/stash-freebsd
|
||||||
|
dist/stash-ui.zip
|
||||||
CHECKSUMS_SHA1
|
CHECKSUMS_SHA1
|
||||||
gzip: false
|
gzip: false
|
||||||
|
|
||||||
|
|
|
||||||
5
Makefile
5
Makefile
|
|
@ -353,6 +353,11 @@ endif
|
||||||
ui: ui-env
|
ui: ui-env
|
||||||
cd ui/v2.5 && yarn build
|
cd ui/v2.5 && yarn build
|
||||||
|
|
||||||
|
.PHONY: zip-ui
|
||||||
|
zip-ui:
|
||||||
|
rm -f dist/stash-ui.zip
|
||||||
|
cd ui/v2.5/build && zip -r ../../../dist/stash-ui.zip .
|
||||||
|
|
||||||
.PHONY: ui-start
|
.PHONY: ui-start
|
||||||
ui-start: ui-env
|
ui-start: ui-env
|
||||||
cd ui/v2.5 && yarn start --host
|
cd ui/v2.5 && yarn start --host
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,28 @@ type Server struct {
|
||||||
manager *manager.Manager
|
manager *manager.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO - os.DirFS doesn't implement ReadDir, so re-implement it here
|
||||||
|
// This can be removed when we upgrade go
|
||||||
|
type osFS string
|
||||||
|
|
||||||
|
func (dir osFS) ReadDir(name string) ([]os.DirEntry, error) {
|
||||||
|
fullname := string(dir) + "/" + name
|
||||||
|
entries, err := os.ReadDir(fullname)
|
||||||
|
if err != nil {
|
||||||
|
var e *os.PathError
|
||||||
|
if errors.As(err, &e) {
|
||||||
|
// See comment in dirFS.Open.
|
||||||
|
e.Path = name
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return entries, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dir osFS) Open(name string) (fs.File, error) {
|
||||||
|
return os.DirFS(string(dir)).Open(name)
|
||||||
|
}
|
||||||
|
|
||||||
// Called at startup
|
// Called at startup
|
||||||
func Initialize() (*Server, error) {
|
func Initialize() (*Server, error) {
|
||||||
mgr := manager.GetInstance()
|
mgr := manager.GetInstance()
|
||||||
|
|
@ -213,25 +235,31 @@ func Initialize() (*Server, error) {
|
||||||
r.Mount("/custom", getCustomRoutes(customServedFolders))
|
r.Mount("/custom", getCustomRoutes(customServedFolders))
|
||||||
}
|
}
|
||||||
|
|
||||||
customUILocation := cfg.GetCustomUILocation()
|
var uiFS fs.FS
|
||||||
staticUI := statigz.FileServer(ui.UIBox.(fs.ReadDirFS))
|
var staticUI *statigz.Server
|
||||||
|
customUILocation := cfg.GetUILocation()
|
||||||
|
if customUILocation != "" {
|
||||||
|
logger.Debugf("Serving UI from %s", customUILocation)
|
||||||
|
uiFS = osFS(customUILocation)
|
||||||
|
staticUI = statigz.FileServer(uiFS.(fs.ReadDirFS))
|
||||||
|
} else {
|
||||||
|
logger.Debug("Serving embedded UI")
|
||||||
|
uiFS = ui.UIBox
|
||||||
|
staticUI = statigz.FileServer(ui.UIBox.(fs.ReadDirFS))
|
||||||
|
}
|
||||||
|
|
||||||
// Serve the web app
|
// Serve the web app
|
||||||
r.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {
|
r.HandleFunc("/*", func(w http.ResponseWriter, r *http.Request) {
|
||||||
ext := path.Ext(r.URL.Path)
|
ext := path.Ext(r.URL.Path)
|
||||||
|
|
||||||
if customUILocation != "" {
|
if ext == ".html" || ext == "" {
|
||||||
if r.URL.Path == "index.html" || ext == "" {
|
w.Header().Set("Content-Type", "text/html")
|
||||||
r.URL.Path = "/"
|
setPageSecurityHeaders(w, r, pluginCache.ListPlugins())
|
||||||
}
|
|
||||||
|
|
||||||
http.FileServer(http.Dir(customUILocation)).ServeHTTP(w, r)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ext == ".html" || ext == "" {
|
if ext == "" || r.URL.Path == "/" || r.URL.Path == "/index.html" {
|
||||||
themeColor := cfg.GetThemeColor()
|
themeColor := cfg.GetThemeColor()
|
||||||
data, err := fs.ReadFile(ui.UIBox, "index.html")
|
data, err := fs.ReadFile(uiFS, "index.html")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -241,9 +269,6 @@ func Initialize() (*Server, error) {
|
||||||
indexHtml = strings.ReplaceAll(indexHtml, "%COLOR%", themeColor)
|
indexHtml = strings.ReplaceAll(indexHtml, "%COLOR%", themeColor)
|
||||||
indexHtml = strings.Replace(indexHtml, `<base href="/"`, fmt.Sprintf(`<base href="%s/"`, prefix), 1)
|
indexHtml = strings.Replace(indexHtml, `<base href="/"`, fmt.Sprintf(`<base href="%s/"`, prefix), 1)
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html")
|
|
||||||
setPageSecurityHeaders(w, r, pluginCache.ListPlugins())
|
|
||||||
|
|
||||||
utils.ServeStaticContent(w, r, []byte(indexHtml))
|
utils.ServeStaticContent(w, r, []byte(indexHtml))
|
||||||
} else {
|
} else {
|
||||||
isStatic, _ := path.Match("/assets/*", r.URL.Path)
|
isStatic, _ := path.Match("/assets/*", r.URL.Path)
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,10 @@ const (
|
||||||
|
|
||||||
// UI directory. Overrides to serve the UI from a specific location
|
// UI directory. Overrides to serve the UI from a specific location
|
||||||
// rather than use the embedded UI.
|
// rather than use the embedded UI.
|
||||||
CustomUILocation = "custom_ui_location"
|
UILocation = "ui_location"
|
||||||
|
|
||||||
|
// backwards compatible name
|
||||||
|
LegacyCustomUILocation = "custom_ui_location"
|
||||||
|
|
||||||
// Gallery Cover Regex
|
// Gallery Cover Regex
|
||||||
GalleryCoverRegex = "gallery_cover_regex"
|
GalleryCoverRegex = "gallery_cover_regex"
|
||||||
|
|
@ -1057,8 +1060,12 @@ func (i *Config) GetCustomServedFolders() utils.URLMap {
|
||||||
return i.getStringMapString(CustomServedFolders)
|
return i.getStringMapString(CustomServedFolders)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Config) GetCustomUILocation() string {
|
func (i *Config) GetUILocation() string {
|
||||||
return i.getString(CustomUILocation)
|
if ret := i.getString(UILocation); ret != "" {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
return i.getString(LegacyCustomUILocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interface options
|
// Interface options
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func TestConcurrentConfigAccess(t *testing.T) {
|
||||||
i.GetCredentials()
|
i.GetCredentials()
|
||||||
i.Set(MaxSessionAge, i.GetMaxSessionAge())
|
i.Set(MaxSessionAge, i.GetMaxSessionAge())
|
||||||
i.Set(CustomServedFolders, i.GetCustomServedFolders())
|
i.Set(CustomServedFolders, i.GetCustomServedFolders())
|
||||||
i.Set(CustomUILocation, i.GetCustomUILocation())
|
i.Set(LegacyCustomUILocation, i.GetUILocation())
|
||||||
i.Set(MenuItems, i.GetMenuItems())
|
i.Set(MenuItems, i.GetMenuItems())
|
||||||
i.Set(SoundOnPreview, i.GetSoundOnPreview())
|
i.Set(SoundOnPreview, i.GetSoundOnPreview())
|
||||||
i.Set(WallShowTitle, i.GetWallShowTitle())
|
i.Set(WallShowTitle, i.GetWallShowTitle())
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
@ -26,6 +27,7 @@ func init() {
|
||||||
pflag.Int("port", 9999, "port to serve from")
|
pflag.Int("port", 9999, "port to serve from")
|
||||||
pflag.StringVarP(&flags.configFilePath, "config", "c", "", "config file to use")
|
pflag.StringVarP(&flags.configFilePath, "config", "c", "", "config file to use")
|
||||||
pflag.BoolVar(&flags.nobrowser, "nobrowser", false, "Don't open a browser window after launch")
|
pflag.BoolVar(&flags.nobrowser, "nobrowser", false, "Don't open a browser window after launch")
|
||||||
|
pflag.StringP("ui-location", "u", "", "path to the webui")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called at startup
|
// Called at startup
|
||||||
|
|
@ -82,8 +84,8 @@ func InitializeEmpty() *Config {
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
|
|
||||||
func bindEnv(v *viper.Viper, key string) {
|
func bindEnv(v *viper.Viper, key ...string) {
|
||||||
if err := v.BindEnv(key); err != nil {
|
if err := v.BindEnv(key...); err != nil {
|
||||||
panic(fmt.Sprintf("unable to set environment key (%v): %v", key, err))
|
panic(fmt.Sprintf("unable to set environment key (%v): %v", key, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,18 +93,27 @@ func bindEnv(v *viper.Viper, key string) {
|
||||||
func (i *Config) initOverrides() {
|
func (i *Config) initOverrides() {
|
||||||
v := i.overrides
|
v := i.overrides
|
||||||
|
|
||||||
|
// replace dashes with underscores in the flag names
|
||||||
|
normalizeFn := pflag.CommandLine.GetNormalizeFunc()
|
||||||
|
pflag.CommandLine.SetNormalizeFunc(func(f *pflag.FlagSet, name string) pflag.NormalizedName {
|
||||||
|
result := normalizeFn(f, name)
|
||||||
|
name = strings.ReplaceAll(string(result), "-", "_")
|
||||||
|
return pflag.NormalizedName(name)
|
||||||
|
})
|
||||||
|
|
||||||
if err := v.BindPFlags(pflag.CommandLine); err != nil {
|
if err := v.BindPFlags(pflag.CommandLine); err != nil {
|
||||||
logger.Infof("failed to bind flags: %v", err)
|
logger.Infof("failed to bind flags: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
v.SetEnvPrefix("stash") // will be uppercased automatically
|
v.SetEnvPrefix("stash") // will be uppercased automatically
|
||||||
bindEnv(v, "host") // STASH_HOST
|
bindEnv(v, "host") // STASH_HOST
|
||||||
bindEnv(v, "port") // STASH_PORT
|
bindEnv(v, "port") // STASH_PORT
|
||||||
bindEnv(v, "external_host") // STASH_EXTERNAL_HOST
|
bindEnv(v, "external_host") // STASH_EXTERNAL_HOST
|
||||||
bindEnv(v, "generated") // STASH_GENERATED
|
bindEnv(v, "generated") // STASH_GENERATED
|
||||||
bindEnv(v, "metadata") // STASH_METADATA
|
bindEnv(v, "metadata") // STASH_METADATA
|
||||||
bindEnv(v, "cache") // STASH_CACHE
|
bindEnv(v, "cache") // STASH_CACHE
|
||||||
bindEnv(v, "stash") // STASH_STASH
|
bindEnv(v, "stash") // STASH_STASH
|
||||||
|
bindEnv(v, "ui_location", "STASH_UI") // STASH_UI
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Config) initConfig() error {
|
func (i *Config) initConfig() error {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue