diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index b5066de964..4c0812a13d 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -2186,6 +2186,7 @@ LEVEL = Info ;ENABLE_SUCCESS_NOTICE = false ;SCHEDULE = @every 168h ;HTTP_ENDPOINT = https://dl.gitea.com/gitea/version.json +;DOMAIN_ENDPOINT = release.forgejo.org ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/modules/updatechecker/update_checker.go b/modules/updatechecker/update_checker.go index bc3f93aad7..d3008ab122 100644 --- a/modules/updatechecker/update_checker.go +++ b/modules/updatechecker/update_checker.go @@ -4,8 +4,11 @@ package updatechecker import ( + "errors" "io" + "net" "net/http" + "strings" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/proxy" @@ -26,7 +29,51 @@ func (r *CheckerState) Name() string { } // GiteaUpdateChecker returns error when new version of Gitea is available -func GiteaUpdateChecker(httpEndpoint string) error { +func GiteaUpdateChecker(httpEndpoint, domainEndpoint string) error { + var version string + var err error + if domainEndpoint != "" { + version, err = getVersionDNS(domainEndpoint) + } else { + version, err = getVersionHTTP(httpEndpoint) + } + + if err != nil { + return err + } + + return UpdateRemoteVersion(version) +} + +// getVersionDNS will request the TXT records for the domain. If a record starts +// with "forgejo_versions=" everything after that will be used as the latest +// version available. +func getVersionDNS(domainEndpoint string) (version string, err error) { + records, err := net.LookupTXT(domainEndpoint) + if err != nil { + return "", err + } + + if len(records) == 0 { + return "", errors.New("no TXT records were found") + } + + for _, record := range records { + if strings.HasPrefix(record, "forgejo_versions=") { + // Get all supported versions, separated by a comma. + supportedVersions := strings.Split(strings.TrimPrefix(record, "forgejo_versions="), ",") + // For now always return the latest supported version. + return supportedVersions[len(supportedVersions)-1], nil + } + } + + return "", errors.New("there is no TXT record with a valid value") +} + +// getVersionHTTP will make an HTTP request to the endpoint, and the returned +// content is JSON. The "latest.version" path's value will be used as the latest +// version available. +func getVersionHTTP(httpEndpoint string) (version string, err error) { httpClient := &http.Client{ Transport: &http.Transport{ Proxy: proxy.Proxy(), @@ -35,16 +82,16 @@ func GiteaUpdateChecker(httpEndpoint string) error { req, err := http.NewRequest("GET", httpEndpoint, nil) if err != nil { - return err + return "", err } resp, err := httpClient.Do(req) if err != nil { - return err + return "", err } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { - return err + return "", err } type respType struct { @@ -55,10 +102,9 @@ func GiteaUpdateChecker(httpEndpoint string) error { respData := respType{} err = json.Unmarshal(body, &respData) if err != nil { - return err + return "", err } - - return UpdateRemoteVersion(respData.Latest.Version) + return respData.Latest.Version, nil } // UpdateRemoteVersion updates the latest available version of Gitea diff --git a/modules/updatechecker/update_checker_test.go b/modules/updatechecker/update_checker_test.go new file mode 100644 index 0000000000..301afd95e4 --- /dev/null +++ b/modules/updatechecker/update_checker_test.go @@ -0,0 +1,16 @@ +// Copyright 2023 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package updatechecker + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestDNSUpdate(t *testing.T) { + version, err := getVersionDNS("release.forgejo.org") + assert.NoError(t, err) + assert.NotEmpty(t, version) +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index c59e358e2d..16fb347e6b 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -286,6 +286,7 @@ run_user_not_match = The 'run as' username is not the current username: %s -> %s internal_token_failed = Failed to generate internal token: %v secret_key_failed = Failed to generate secret key: %v save_config_failed = Failed to save configuration: %v +enable_update_checker_helper_forgejo = Periodically checks for new Forgejo versions by checking a DNS TXT record at release.forgejo.org. invalid_admin_setting = Administrator account setting is invalid: %v invalid_log_root_path = The log path is invalid: %v default_keep_email_private = Hide Email Addresses by Default diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 4abf44cdb1..569e8fbd24 100644 --- a/services/cron/tasks_extended.go +++ b/services/cron/tasks_extended.go @@ -142,7 +142,8 @@ func registerDeleteOldActions() { func registerUpdateGiteaChecker() { type UpdateCheckerConfig struct { BaseConfig - HTTPEndpoint string + HTTPEndpoint string + DomainEndpoint string } RegisterTaskFatal("update_checker", &UpdateCheckerConfig{ BaseConfig: BaseConfig{ @@ -150,10 +151,11 @@ func registerUpdateGiteaChecker() { RunAtStart: false, Schedule: "@every 168h", }, - HTTPEndpoint: "https://dl.gitea.com/gitea/version.json", + HTTPEndpoint: "https://dl.gitea.com/gitea/version.json", + DomainEndpoint: "release.forgejo.org", }, func(ctx context.Context, _ *user_model.User, config Config) error { updateCheckerConfig := config.(*UpdateCheckerConfig) - return updatechecker.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint) + return updatechecker.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint, updateCheckerConfig.DomainEndpoint) }) } diff --git a/templates/install.tmpl b/templates/install.tmpl index e9b267fa1c..fda07fca68 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -152,7 +152,7 @@ - {{ctx.Locale.Tr "install.enable_update_checker_helper"}} + {{ctx.Locale.Tr "install.enable_update_checker_helper_forgejo"}}