mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 12:24:38 +03:00
Add additional latest version info (#3357)
This commit is contained in:
@@ -67,7 +67,9 @@ query Version {
|
|||||||
|
|
||||||
query LatestVersion {
|
query LatestVersion {
|
||||||
latestversion {
|
latestversion {
|
||||||
|
version
|
||||||
shorthash
|
shorthash
|
||||||
|
release_date
|
||||||
url
|
url
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ type Query {
|
|||||||
version: Version!
|
version: Version!
|
||||||
|
|
||||||
# LatestVersion
|
# LatestVersion
|
||||||
latestversion: ShortVersion!
|
latestversion: LatestVersion!
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ type Version {
|
|||||||
build_time: String!
|
build_time: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type ShortVersion {
|
type LatestVersion {
|
||||||
|
version: String!
|
||||||
shorthash: String!
|
shorthash: String!
|
||||||
|
release_date: String!
|
||||||
url: String!
|
url: String!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,12 +20,7 @@ import (
|
|||||||
const apiReleases string = "https://api.github.com/repos/stashapp/stash/releases"
|
const apiReleases string = "https://api.github.com/repos/stashapp/stash/releases"
|
||||||
const apiTags string = "https://api.github.com/repos/stashapp/stash/tags"
|
const apiTags string = "https://api.github.com/repos/stashapp/stash/tags"
|
||||||
const apiAcceptHeader string = "application/vnd.github.v3+json"
|
const apiAcceptHeader string = "application/vnd.github.v3+json"
|
||||||
const developmentTag string = "latest_develop"
|
const defaultSHLength int = 8 // default length of SHA short hash returned by <git rev-parse --short HEAD>
|
||||||
const defaultSHLength int = 7 // default length of SHA short hash returned by <git rev-parse --short HEAD>
|
|
||||||
|
|
||||||
// ErrNoVersion indicates that no version information has been embedded in the
|
|
||||||
// stash binary
|
|
||||||
var ErrNoVersion = errors.New("no stash version")
|
|
||||||
|
|
||||||
var stashReleases = func() map[string]string {
|
var stashReleases = func() map[string]string {
|
||||||
return map[string]string{
|
return map[string]string{
|
||||||
@@ -108,6 +103,14 @@ type githubTagResponse struct {
|
|||||||
Node_id string
|
Node_id string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LatestRelease struct {
|
||||||
|
Version string
|
||||||
|
Hash string
|
||||||
|
ShortHash string
|
||||||
|
Date string
|
||||||
|
Url string
|
||||||
|
}
|
||||||
|
|
||||||
func makeGithubRequest(ctx context.Context, url string, output interface{}) error {
|
func makeGithubRequest(ctx context.Context, url string, output interface{}) error {
|
||||||
|
|
||||||
transport := &http.Transport{Proxy: http.ProxyFromEnvironment}
|
transport := &http.Transport{Proxy: http.ProxyFromEnvironment}
|
||||||
@@ -148,14 +151,16 @@ func makeGithubRequest(ctx context.Context, url string, output interface{}) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLatestVersion gets latest version (git commit hash) from github API
|
// GetLatestRelease gets latest release information from github API
|
||||||
// If running a build from the "master" branch, then the latest full release
|
// If running a build from the "master" branch, then the latest full release
|
||||||
// is used, otherwise it uses the release that is tagged with "latest_develop"
|
// is used, otherwise it uses the release that is tagged with "latest_develop"
|
||||||
// which is the latest pre-release build.
|
// which is the latest pre-release build.
|
||||||
func GetLatestVersion(ctx context.Context, shortHash bool) (latestVersion string, latestRelease string, err error) {
|
func GetLatestRelease(ctx context.Context) (*LatestRelease, error) {
|
||||||
|
arch := runtime.GOARCH
|
||||||
|
|
||||||
arch := runtime.GOARCH // https://en.wikipedia.org/wiki/Comparison_of_ARM_cores
|
// https://en.wikipedia.org/wiki/Comparison_of_ARM_cores
|
||||||
isARMv7 := cpu.ARM.HasNEON || cpu.ARM.HasVFPv3 || cpu.ARM.HasVFPv3D16 || cpu.ARM.HasVFPv4 // armv6 doesn't support any of these features
|
// armv6 doesn't support any of these features
|
||||||
|
isARMv7 := cpu.ARM.HasNEON || cpu.ARM.HasVFPv3 || cpu.ARM.HasVFPv3D16 || cpu.ARM.HasVFPv4
|
||||||
if arch == "arm" && isARMv7 {
|
if arch == "arm" && isARMv7 {
|
||||||
arch = "armv7"
|
arch = "armv7"
|
||||||
}
|
}
|
||||||
@@ -163,125 +168,100 @@ func GetLatestVersion(ctx context.Context, shortHash bool) (latestVersion string
|
|||||||
platform := fmt.Sprintf("%s/%s", runtime.GOOS, arch)
|
platform := fmt.Sprintf("%s/%s", runtime.GOOS, arch)
|
||||||
wantedRelease := stashReleases()[platform]
|
wantedRelease := stashReleases()[platform]
|
||||||
|
|
||||||
version, _, _ := GetVersion()
|
var release githubReleasesResponse
|
||||||
if version == "" {
|
if IsDevelop() {
|
||||||
return "", "", ErrNoVersion
|
// get the latest release, prerelease or not
|
||||||
}
|
releases := []githubReleasesResponse{}
|
||||||
|
err := makeGithubRequest(ctx, apiReleases+"?per_page=1", &releases)
|
||||||
// if the version is suffixed with -x-xxxx, then we are running a development build
|
if err != nil {
|
||||||
usePreRelease := false
|
return nil, err
|
||||||
re := regexp.MustCompile(`-\d+-g\w+$`)
|
}
|
||||||
if re.MatchString(version) {
|
release = releases[0]
|
||||||
usePreRelease = true
|
|
||||||
}
|
|
||||||
|
|
||||||
url := apiReleases
|
|
||||||
if !usePreRelease {
|
|
||||||
// just get the latest full release
|
|
||||||
url += "/latest"
|
|
||||||
} else {
|
} else {
|
||||||
// get the release tagged with the development tag
|
// just get the latest full release
|
||||||
url += "/tags/" + developmentTag
|
err := makeGithubRequest(ctx, apiReleases+"/latest", &release)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
release := githubReleasesResponse{}
|
version := release.Name
|
||||||
err = makeGithubRequest(ctx, url, &release)
|
if release.Prerelease {
|
||||||
|
// find version in prerelease name
|
||||||
|
re := regexp.MustCompile(`v[\w-\.]+-\d+-g[0-9a-f]+`)
|
||||||
|
if match := re.FindString(version); match != "" {
|
||||||
|
version = match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
latestHash, err := getReleaseHash(ctx, release.Tag_name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if release.Prerelease == usePreRelease {
|
var releaseDate string
|
||||||
latestVersion = getReleaseHash(ctx, release, shortHash, usePreRelease)
|
if publishedAt, err := time.Parse(time.RFC3339, release.Published_at); err == nil {
|
||||||
|
releaseDate = publishedAt.Format("2006-01-02")
|
||||||
|
}
|
||||||
|
|
||||||
if wantedRelease != "" {
|
var releaseUrl string
|
||||||
for _, asset := range release.Assets {
|
if wantedRelease != "" {
|
||||||
if asset.Name == wantedRelease {
|
for _, asset := range release.Assets {
|
||||||
latestRelease = asset.Browser_download_url
|
if asset.Name == wantedRelease {
|
||||||
break
|
releaseUrl = asset.Browser_download_url
|
||||||
}
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if latestVersion == "" {
|
_, githash, _ := GetVersion()
|
||||||
return "", "", fmt.Errorf("no version found for \"%s\"", version)
|
shLength := len(githash)
|
||||||
|
if shLength == 0 {
|
||||||
|
shLength = defaultSHLength
|
||||||
}
|
}
|
||||||
return latestVersion, latestRelease, nil
|
|
||||||
|
return &LatestRelease{
|
||||||
|
Version: version,
|
||||||
|
Hash: latestHash,
|
||||||
|
ShortHash: latestHash[:shLength],
|
||||||
|
Date: releaseDate,
|
||||||
|
Url: releaseUrl,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getReleaseHash(ctx context.Context, release githubReleasesResponse, shortHash bool, usePreRelease bool) string {
|
func getReleaseHash(ctx context.Context, tagName string) (string, error) {
|
||||||
shaLength := len(release.Target_commitish)
|
|
||||||
// the /latest API call doesn't return the hash in target_commitish
|
|
||||||
// also add sanity check in case Target_commitish is not 40 characters
|
|
||||||
if !usePreRelease || shaLength != 40 {
|
|
||||||
return getShaFromTags(ctx, shortHash, release.Tag_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if shortHash {
|
|
||||||
last := defaultSHLength // default length of git short hash
|
|
||||||
_, gitShort, _ := GetVersion() // retrieve it to check actual length
|
|
||||||
if len(gitShort) > last && len(gitShort) < shaLength { // sometimes short hash is longer
|
|
||||||
last = len(gitShort)
|
|
||||||
}
|
|
||||||
return release.Target_commitish[0:last]
|
|
||||||
}
|
|
||||||
|
|
||||||
return release.Target_commitish
|
|
||||||
}
|
|
||||||
|
|
||||||
func printLatestVersion(ctx context.Context) {
|
|
||||||
_, githash, _ = GetVersion()
|
|
||||||
latest, _, err := GetLatestVersion(ctx, true)
|
|
||||||
if err != nil {
|
|
||||||
logger.Errorf("Couldn't find latest version: %s", err)
|
|
||||||
} else {
|
|
||||||
if githash == latest {
|
|
||||||
logger.Infof("Version: (%s) is already the latest released.", latest)
|
|
||||||
} else {
|
|
||||||
logger.Infof("New version: (%s) available.", latest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get sha from the github api tags endpoint
|
|
||||||
// returns the sha1 hash/shorthash or "" if something's wrong
|
|
||||||
func getShaFromTags(ctx context.Context, shortHash bool, name string) string {
|
|
||||||
url := apiTags
|
url := apiTags
|
||||||
tags := []githubTagResponse{}
|
tags := []githubTagResponse{}
|
||||||
err := makeGithubRequest(ctx, url, &tags)
|
err := makeGithubRequest(ctx, url, &tags)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// If the context is canceled, we don't want to log this as an error
|
return "", err
|
||||||
// in the path. The function here just gives up and returns "" if
|
|
||||||
// something goes wrong. Hence, log the error at the info-level so
|
|
||||||
// it's still present, but don't treat this as an error.
|
|
||||||
if errors.Is(err, context.Canceled) {
|
|
||||||
logger.Infof("aborting sha request due to context cancellation")
|
|
||||||
} else {
|
|
||||||
logger.Errorf("Github Tags Api: %v", err)
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
_, gitShort, _ := GetVersion() // retrieve short hash to check actual length
|
|
||||||
|
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
if tag.Name == name {
|
if tag.Name == tagName {
|
||||||
shaLength := len(tag.Commit.Sha)
|
if len(tag.Commit.Sha) != 40 {
|
||||||
if shaLength != 40 {
|
return "", errors.New("invalid Github API response")
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
if shortHash {
|
return tag.Commit.Sha, nil
|
||||||
last := defaultSHLength // default length of git short hash
|
|
||||||
if len(gitShort) > last && len(gitShort) < shaLength { // sometimes short hash is longer
|
|
||||||
last = len(gitShort)
|
|
||||||
}
|
|
||||||
return tag.Commit.Sha[0:last]
|
|
||||||
}
|
|
||||||
|
|
||||||
return tag.Commit.Sha
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return "", errors.New("invalid Github API response")
|
||||||
|
}
|
||||||
|
|
||||||
|
func printLatestVersion(ctx context.Context) {
|
||||||
|
latestRelease, err := GetLatestRelease(ctx)
|
||||||
|
if err != nil {
|
||||||
|
logger.Errorf("Couldn't retrieve latest version: %v", err)
|
||||||
|
} else {
|
||||||
|
_, githash, _ = GetVersion()
|
||||||
|
switch {
|
||||||
|
case githash == "":
|
||||||
|
logger.Infof("Latest version: %s (%s)", latestRelease.Version, latestRelease.ShortHash)
|
||||||
|
case githash == latestRelease.ShortHash:
|
||||||
|
logger.Infof("Version %s (%s) is already the latest released", latestRelease.Version, latestRelease.ShortHash)
|
||||||
|
default:
|
||||||
|
logger.Infof("New version available: %s (%s)", latestRelease.Version, latestRelease.ShortHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,19 +184,22 @@ func (r *queryResolver) Version(ctx context.Context) (*Version, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Latestversion returns the latest git shorthash commit.
|
func (r *queryResolver) Latestversion(ctx context.Context) (*LatestVersion, error) {
|
||||||
func (r *queryResolver) Latestversion(ctx context.Context) (*ShortVersion, error) {
|
latestRelease, err := GetLatestRelease(ctx)
|
||||||
ver, url, err := GetLatestVersion(ctx, true)
|
if err != nil {
|
||||||
if err == nil {
|
if !errors.Is(err, context.Canceled) {
|
||||||
logger.Infof("Retrieved latest hash: %s", ver)
|
logger.Errorf("Error while retrieving latest version: %v", err)
|
||||||
} else {
|
}
|
||||||
logger.Errorf("Error while retrieving latest hash: %s", err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
logger.Infof("Retrieved latest version: %s (%s)", latestRelease.Version, latestRelease.ShortHash)
|
||||||
|
|
||||||
return &ShortVersion{
|
return &LatestVersion{
|
||||||
Shorthash: ver,
|
Version: latestRelease.Version,
|
||||||
URL: url,
|
Shorthash: latestRelease.ShortHash,
|
||||||
}, err
|
ReleaseDate: latestRelease.Date,
|
||||||
|
URL: latestRelease.Url,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get scene marker tags which show up under the video.
|
// Get scene marker tags which show up under the video.
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"regexp"
|
||||||
"runtime/debug"
|
"runtime/debug"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -392,22 +393,48 @@ func javascriptHandler(c *config.Instance, pluginCache *plugin.Cache) func(w htt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printVersion() {
|
func printVersion() {
|
||||||
versionString := githash
|
var versionString string
|
||||||
|
switch {
|
||||||
|
case version != "":
|
||||||
|
if githash != "" && !IsDevelop() {
|
||||||
|
versionString = version + " (" + githash + ")"
|
||||||
|
} else {
|
||||||
|
versionString = version
|
||||||
|
}
|
||||||
|
case githash != "":
|
||||||
|
versionString = githash
|
||||||
|
default:
|
||||||
|
versionString = "unknown"
|
||||||
|
}
|
||||||
if config.IsOfficialBuild() {
|
if config.IsOfficialBuild() {
|
||||||
versionString += " - Official Build"
|
versionString += " - Official Build"
|
||||||
} else {
|
} else {
|
||||||
versionString += " - Unofficial Build"
|
versionString += " - Unofficial Build"
|
||||||
}
|
}
|
||||||
if version != "" {
|
if buildstamp != "" {
|
||||||
versionString = version + " (" + versionString + ")"
|
versionString += " - " + buildstamp
|
||||||
}
|
}
|
||||||
fmt.Printf("stash version: %s - %s\n", versionString, buildstamp)
|
logger.Infof("stash version: %s\n", versionString)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetVersion() (string, string, string) {
|
func GetVersion() (string, string, string) {
|
||||||
return version, githash, buildstamp
|
return version, githash, buildstamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsDevelop() bool {
|
||||||
|
if githash == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the version is suffixed with -x-xxxx, then we are running a development build
|
||||||
|
develop := false
|
||||||
|
re := regexp.MustCompile(`-\d+-g\w+$`)
|
||||||
|
if re.MatchString(version) {
|
||||||
|
develop = true
|
||||||
|
}
|
||||||
|
return develop
|
||||||
|
}
|
||||||
|
|
||||||
func makeTLSConfig(c *config.Instance) (*tls.Config, error) {
|
func makeTLSConfig(c *config.Instance) (*tls.Config, error) {
|
||||||
c.InitTLS()
|
c.InitTLS()
|
||||||
certFile, keyFile := c.GetTLSFiles()
|
certFile, keyFile := c.GetTLSFiles()
|
||||||
@@ -428,12 +455,12 @@ func makeTLSConfig(c *config.Instance) (*tls.Config, error) {
|
|||||||
|
|
||||||
cert, err := os.ReadFile(certFile)
|
cert, err := os.ReadFile(certFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error reading SSL certificate file %s: %s", certFile, err.Error())
|
return nil, fmt.Errorf("error reading SSL certificate file %s: %v", certFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
key, err := os.ReadFile(keyFile)
|
key, err := os.ReadFile(keyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error reading SSL key file %s: %s", keyFile, err.Error())
|
return nil, fmt.Errorf("error reading SSL key file %s: %v", keyFile, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
certs := make([]tls.Certificate, 1)
|
certs := make([]tls.Certificate, 1)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React from "react";
|
|||||||
import { Button } from "react-bootstrap";
|
import { Button } from "react-bootstrap";
|
||||||
import { useIntl } from "react-intl";
|
import { useIntl } from "react-intl";
|
||||||
import { useLatestVersion } from "src/core/StashService";
|
import { useLatestVersion } from "src/core/StashService";
|
||||||
import { ConstantSetting, Setting, SettingGroup } from "./Inputs";
|
import { ConstantSetting, SettingGroup } from "./Inputs";
|
||||||
import { SettingSection } from "./SettingSection";
|
import { SettingSection } from "./SettingSection";
|
||||||
|
|
||||||
export const SettingsAboutPanel: React.FC = () => {
|
export const SettingsAboutPanel: React.FC = () => {
|
||||||
@@ -20,7 +20,69 @@ export const SettingsAboutPanel: React.FC = () => {
|
|||||||
networkStatus,
|
networkStatus,
|
||||||
} = useLatestVersion();
|
} = useLatestVersion();
|
||||||
|
|
||||||
const hasNew = dataLatest && gitHash !== dataLatest.latestversion.shorthash;
|
function renderLatestVersion() {
|
||||||
|
if (errorLatest) {
|
||||||
|
return (
|
||||||
|
<SettingGroup
|
||||||
|
settingProps={{
|
||||||
|
heading: errorLatest.message,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (!dataLatest || loadingLatest || networkStatus === 4) {
|
||||||
|
return (
|
||||||
|
<SettingGroup
|
||||||
|
settingProps={{
|
||||||
|
headingID: "loading.generic",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let heading = dataLatest.latestversion.version;
|
||||||
|
const hashString = dataLatest.latestversion.shorthash;
|
||||||
|
if (gitHash !== hashString) {
|
||||||
|
heading +=
|
||||||
|
" " +
|
||||||
|
intl.formatMessage({
|
||||||
|
id: "config.about.new_version_notice",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<SettingGroup
|
||||||
|
settingProps={{
|
||||||
|
heading,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="setting">
|
||||||
|
<div>
|
||||||
|
<h3>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "config.about.build_hash",
|
||||||
|
})}
|
||||||
|
</h3>
|
||||||
|
<div className="value">{hashString}</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a href={dataLatest.latestversion.url}>
|
||||||
|
<Button>
|
||||||
|
{intl.formatMessage({ id: "actions.download" })}
|
||||||
|
</Button>
|
||||||
|
</a>
|
||||||
|
<Button onClick={() => refetch()}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "config.about.check_for_new_version",
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ConstantSetting
|
||||||
|
headingID="config.about.release_date"
|
||||||
|
value={dataLatest.latestversion.release_date}
|
||||||
|
/>
|
||||||
|
</SettingGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -39,48 +101,10 @@ export const SettingsAboutPanel: React.FC = () => {
|
|||||||
value={buildTime}
|
value={buildTime}
|
||||||
/>
|
/>
|
||||||
</SettingGroup>
|
</SettingGroup>
|
||||||
|
</SettingSection>
|
||||||
|
|
||||||
<SettingGroup
|
<SettingSection headingID="config.about.latest_version">
|
||||||
settingProps={{
|
{renderLatestVersion()}
|
||||||
headingID: "config.about.latest_version",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{errorLatest ? (
|
|
||||||
<Setting heading={errorLatest.message} />
|
|
||||||
) : !dataLatest || loadingLatest || networkStatus === 4 ? (
|
|
||||||
<Setting headingID="loading.generic" />
|
|
||||||
) : (
|
|
||||||
<div className="setting">
|
|
||||||
<div>
|
|
||||||
<h3>
|
|
||||||
{intl.formatMessage({
|
|
||||||
id: "config.about.latest_version_build_hash",
|
|
||||||
})}
|
|
||||||
</h3>
|
|
||||||
<div className="value">
|
|
||||||
{dataLatest.latestversion.shorthash}{" "}
|
|
||||||
{hasNew
|
|
||||||
? intl.formatMessage({
|
|
||||||
id: "config.about.new_version_notice",
|
|
||||||
})
|
|
||||||
: undefined}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<a href={dataLatest.latestversion.url}>
|
|
||||||
<Button>
|
|
||||||
{intl.formatMessage({ id: "actions.download" })}
|
|
||||||
</Button>
|
|
||||||
</a>
|
|
||||||
<Button onClick={() => refetch()}>
|
|
||||||
{intl.formatMessage({
|
|
||||||
id: "config.about.check_for_new_version",
|
|
||||||
})}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</SettingGroup>
|
|
||||||
</SettingSection>
|
</SettingSection>
|
||||||
|
|
||||||
<SettingSection headingID="config.categories.about">
|
<SettingSection headingID="config.categories.about">
|
||||||
|
|||||||
@@ -185,6 +185,7 @@
|
|||||||
"latest_version": "Latest Version",
|
"latest_version": "Latest Version",
|
||||||
"latest_version_build_hash": "Latest Version Build Hash:",
|
"latest_version_build_hash": "Latest Version Build Hash:",
|
||||||
"new_version_notice": "[NEW]",
|
"new_version_notice": "[NEW]",
|
||||||
|
"release_date": "Release date:",
|
||||||
"stash_discord": "Join our {url} channel",
|
"stash_discord": "Join our {url} channel",
|
||||||
"stash_home": "Stash home at {url}",
|
"stash_home": "Stash home at {url}",
|
||||||
"stash_open_collective": "Support us through {url}",
|
"stash_open_collective": "Support us through {url}",
|
||||||
|
|||||||
Reference in New Issue
Block a user