Add simple update checker to Gitea (#17212)
* Add simple update checker to Gitea * update struct and remove comments * fix lint * Update custom/conf/app.example.ini * Update docs/content/doc/advanced/config-cheat-sheet.en-us.md Co-authored-by: delvh <dev.lh@web.de> * Update custom/conf/app.example.ini Co-authored-by: delvh <dev.lh@web.de> * Update docs/content/doc/advanced/config-cheat-sheet.en-us.md Co-authored-by: delvh <dev.lh@web.de> * Update docs/content/doc/advanced/config-cheat-sheet.en-us.md Co-authored-by: Steven <61625851+justusbunsi@users.noreply.github.com> * Update docs/content/doc/advanced/config-cheat-sheet.en-us.md * Update modules/cron/tasks_extended.go Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> * Update custom/conf/app.example.ini Co-authored-by: wxiaoguang <wxiaoguang@gmail.com> * take PR feedback into account and display banner on admin dashboard for alerts * Add more detailed message * placate lint * update per feedback Co-authored-by: delvh <dev.lh@web.de> Co-authored-by: Steven <61625851+justusbunsi@users.noreply.github.com> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		
							parent
							
								
									e18ea9e349
								
							
						
					
					
						commit
						8edda8b446
					
				|  | @ -1925,6 +1925,19 @@ PATH = | ||||||
| ;SCHEDULE = @every 168h | ;SCHEDULE = @every 168h | ||||||
| ;OLDER_THAN = 8760h | ;OLDER_THAN = 8760h | ||||||
| 
 | 
 | ||||||
|  | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
|  | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
|  | ;; Check for new Gitea versions | ||||||
|  | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
|  | ;[cron.update_checker] | ||||||
|  | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
|  | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
|  | ;ENABLED = false | ||||||
|  | ;RUN_AT_START = false | ||||||
|  | ;ENABLE_SUCCESS_NOTICE = false | ||||||
|  | ;SCHEDULE = @every 168h | ||||||
|  | ;HTTP_ENDPOINT = https://dl.gitea.io/gitea/version.json | ||||||
|  | 
 | ||||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | ||||||
| ;; Git Operation timeout in seconds | ;; Git Operation timeout in seconds | ||||||
|  |  | ||||||
|  | @ -23,8 +23,8 @@ or any corresponding location. When installing from a distribution, this will | ||||||
| typically be found at `/etc/gitea/conf/app.ini`. | typically be found at `/etc/gitea/conf/app.ini`. | ||||||
| 
 | 
 | ||||||
| The defaults provided here are best-effort (not built automatically). They are | The defaults provided here are best-effort (not built automatically). They are | ||||||
| accurately recorded in [app.example.ini](https://github.com/go-gitea/gitea/blob/master/custom/conf/app.example.ini) | accurately recorded in [app.example.ini](https://github.com/go-gitea/gitea/blob/main/custom/conf/app.example.ini) | ||||||
| (s/master/\<tag|release\>). Any string in the format `%(X)s` is a feature powered | (s/main/\<tag|release\>). Any string in the format `%(X)s` is a feature powered | ||||||
| by [ini](https://github.com/go-ini/ini/#recursive-values), for reading values recursively. | by [ini](https://github.com/go-ini/ini/#recursive-values), for reading values recursively. | ||||||
| 
 | 
 | ||||||
| Values containing `#` or `;` must be quoted using `` ` `` or `"""`. | Values containing `#` or `;` must be quoted using `` ` `` or `"""`. | ||||||
|  | @ -824,9 +824,16 @@ NB: You must have `DISABLE_ROUTER_LOG` set to `false` for this option to take ef | ||||||
| - `ENABLED`: **false**: Enable service. | - `ENABLED`: **false**: Enable service. | ||||||
| - `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED). | - `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED). | ||||||
| - `NO_SUCCESS_NOTICE`: **false**: Set to true to switch off success notices. | - `NO_SUCCESS_NOTICE`: **false**: Set to true to switch off success notices. | ||||||
| - `SCHEDULE`: **@every 128h**: Cron syntax for scheduling a work, e.g. `@every 128h`. | - `SCHEDULE`: **@every 168h**: Cron syntax to set how often to check. | ||||||
| - `OLDER_THAN`: **@every 8760h**: any action older than this expression will be deleted from database, suggest using `8760h` (1 year) because that's the max length of heatmap. | - `OLDER_THAN`: **@every 8760h**: any action older than this expression will be deleted from database, suggest using `8760h` (1 year) because that's the max length of heatmap. | ||||||
| 
 | 
 | ||||||
|  | #### Cron -  Check for new Gitea versions ('cron.update_checker') | ||||||
|  | - `ENABLED`: **false**: Enable service. | ||||||
|  | - `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED). | ||||||
|  | - `ENABLE_SUCCESS_NOTICE`: **true**: Set to false to switch off success notices. | ||||||
|  | - `SCHEDULE`: **@every 168h**: Cron syntax for scheduling a work, e.g. `@every 168h`. | ||||||
|  | - `HTTP_ENDPOINT`: **https://dl.gitea.io/gitea/version.json**: the endpoint that Gitea will check for newer versions | ||||||
|  | 
 | ||||||
| ## Git (`git`) | ## Git (`git`) | ||||||
| 
 | 
 | ||||||
| - `PATH`: **""**: The path of git executable. If empty, Gitea searches through the PATH environment. | - `PATH`: **""**: The path of git executable. If empty, Gitea searches through the PATH environment. | ||||||
|  |  | ||||||
|  | @ -350,6 +350,8 @@ var migrations = []Migration{ | ||||||
| 	NewMigration("Add renamed_branch table", addRenamedBranchTable), | 	NewMigration("Add renamed_branch table", addRenamedBranchTable), | ||||||
| 	// v198 -> v199
 | 	// v198 -> v199
 | ||||||
| 	NewMigration("Add issue content history table", addTableIssueContentHistory), | 	NewMigration("Add issue content history table", addTableIssueContentHistory), | ||||||
|  | 	// v199 -> v200
 | ||||||
|  | 	NewMigration("Add remote version table", addRemoteVersionTable), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetCurrentDBVersion returns the current db version
 | // GetCurrentDBVersion returns the current db version
 | ||||||
|  |  | ||||||
|  | @ -0,0 +1,23 @@ | ||||||
|  | // Copyright 2021 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package migrations | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"xorm.io/xorm" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func addRemoteVersionTable(x *xorm.Engine) error { | ||||||
|  | 	type RemoteVersion struct { | ||||||
|  | 		ID      int64  `xorm:"pk autoincr"` | ||||||
|  | 		Version string `xorm:"VARCHAR(50)"` | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := x.Sync2(new(RemoteVersion)); err != nil { | ||||||
|  | 		return fmt.Errorf("Sync2: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | @ -0,0 +1,121 @@ | ||||||
|  | // Copyright 2021 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package models | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io/ioutil" | ||||||
|  | 	"net/http" | ||||||
|  | 
 | ||||||
|  | 	"code.gitea.io/gitea/models/db" | ||||||
|  | 	"code.gitea.io/gitea/modules/proxy" | ||||||
|  | 	"code.gitea.io/gitea/modules/setting" | ||||||
|  | 
 | ||||||
|  | 	"github.com/hashicorp/go-version" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // RemoteVersion stores the remote version from the JSON endpoint
 | ||||||
|  | type RemoteVersion struct { | ||||||
|  | 	ID      int64  `xorm:"pk autoincr"` | ||||||
|  | 	Version string `xorm:"VARCHAR(50)"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	db.RegisterModel(new(RemoteVersion)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GiteaUpdateChecker returns error when new version of Gitea is available
 | ||||||
|  | func GiteaUpdateChecker(httpEndpoint string) error { | ||||||
|  | 	httpClient := &http.Client{ | ||||||
|  | 		Transport: &http.Transport{ | ||||||
|  | 			Proxy: proxy.Proxy(), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	req, err := http.NewRequest("GET", httpEndpoint, nil) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	resp, err := httpClient.Do(req) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	defer resp.Body.Close() | ||||||
|  | 	body, err := ioutil.ReadAll(resp.Body) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	type v struct { | ||||||
|  | 		Latest struct { | ||||||
|  | 			Version string `json:"version"` | ||||||
|  | 		} `json:"latest"` | ||||||
|  | 	} | ||||||
|  | 	ver := v{} | ||||||
|  | 	err = json.Unmarshal(body, &ver) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return UpdateRemoteVersion(ver.Latest.Version) | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // UpdateRemoteVersion updates the latest available version of Gitea
 | ||||||
|  | func UpdateRemoteVersion(version string) (err error) { | ||||||
|  | 	sess := db.NewSession(db.DefaultContext) | ||||||
|  | 	defer sess.Close() | ||||||
|  | 	if err = sess.Begin(); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	currentVersion := &RemoteVersion{ID: 1} | ||||||
|  | 	has, err := sess.Get(currentVersion) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("get: %v", err) | ||||||
|  | 	} else if !has { | ||||||
|  | 		currentVersion.ID = 1 | ||||||
|  | 		currentVersion.Version = version | ||||||
|  | 
 | ||||||
|  | 		if _, err = sess.InsertOne(currentVersion); err != nil { | ||||||
|  | 			return fmt.Errorf("insert: %v", err) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, err = sess.Update(&RemoteVersion{ID: 1, Version: version}); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return sess.Commit() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetRemoteVersion returns the current remote version (or currently installed verson if fail to fetch from DB)
 | ||||||
|  | func GetRemoteVersion() string { | ||||||
|  | 	e := db.GetEngine(db.DefaultContext) | ||||||
|  | 	v := &RemoteVersion{ID: 1} | ||||||
|  | 	_, err := e.Get(&v) | ||||||
|  | 	if err != nil { | ||||||
|  | 		// return current version if fail to fetch from DB
 | ||||||
|  | 		return setting.AppVer | ||||||
|  | 	} | ||||||
|  | 	return v.Version | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // GetNeedUpdate returns true whether a newer version of Gitea is available
 | ||||||
|  | func GetNeedUpdate() bool { | ||||||
|  | 	curVer, err := version.NewVersion(setting.AppVer) | ||||||
|  | 	if err != nil { | ||||||
|  | 		// return false to fail silently
 | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	remoteVer, err := version.NewVersion(GetRemoteVersion()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		// return false to fail silently
 | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return curVer.LessThan(remoteVer) | ||||||
|  | } | ||||||
|  | @ -131,6 +131,24 @@ func registerDeleteOldActions() { | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func registerUpdateGiteaChecker() { | ||||||
|  | 	type UpdateCheckerConfig struct { | ||||||
|  | 		BaseConfig | ||||||
|  | 		HTTPEndpoint string | ||||||
|  | 	} | ||||||
|  | 	RegisterTaskFatal("update_checker", &UpdateCheckerConfig{ | ||||||
|  | 		BaseConfig: BaseConfig{ | ||||||
|  | 			Enabled:    true, | ||||||
|  | 			RunAtStart: false, | ||||||
|  | 			Schedule:   "@every 168h", | ||||||
|  | 		}, | ||||||
|  | 		HTTPEndpoint: "https://dl.gitea.io/gitea/version.json", | ||||||
|  | 	}, func(ctx context.Context, _ *models.User, config Config) error { | ||||||
|  | 		updateCheckerConfig := config.(*UpdateCheckerConfig) | ||||||
|  | 		return models.GiteaUpdateChecker(updateCheckerConfig.HTTPEndpoint) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func initExtendedTasks() { | func initExtendedTasks() { | ||||||
| 	registerDeleteInactiveUsers() | 	registerDeleteInactiveUsers() | ||||||
| 	registerDeleteRepositoryArchives() | 	registerDeleteRepositoryArchives() | ||||||
|  | @ -142,4 +160,5 @@ func initExtendedTasks() { | ||||||
| 	registerDeleteMissingRepositories() | 	registerDeleteMissingRepositories() | ||||||
| 	registerRemoveRandomAvatars() | 	registerRemoveRandomAvatars() | ||||||
| 	registerDeleteOldActions() | 	registerDeleteOldActions() | ||||||
|  | 	registerUpdateGiteaChecker() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -125,6 +125,8 @@ func Dashboard(ctx *context.Context) { | ||||||
| 	ctx.Data["PageIsAdmin"] = true | 	ctx.Data["PageIsAdmin"] = true | ||||||
| 	ctx.Data["PageIsAdminDashboard"] = true | 	ctx.Data["PageIsAdminDashboard"] = true | ||||||
| 	ctx.Data["Stats"] = models.GetStatistic() | 	ctx.Data["Stats"] = models.GetStatistic() | ||||||
|  | 	ctx.Data["NeedUpdate"] = models.GetNeedUpdate() | ||||||
|  | 	ctx.Data["RemoteVersion"] = models.GetRemoteVersion() | ||||||
| 	// FIXME: update periodically
 | 	// FIXME: update periodically
 | ||||||
| 	updateSystemStatus() | 	updateSystemStatus() | ||||||
| 	ctx.Data["SysStatus"] = sysStatus | 	ctx.Data["SysStatus"] = sysStatus | ||||||
|  |  | ||||||
|  | @ -3,6 +3,11 @@ | ||||||
| 	{{template "admin/navbar" .}} | 	{{template "admin/navbar" .}} | ||||||
| 	<div class="ui container"> | 	<div class="ui container"> | ||||||
| 		{{template "base/alert" .}} | 		{{template "base/alert" .}} | ||||||
|  | 		{{if .NeedUpdate}} | ||||||
|  | 			<div class="ui negative message flash-error"> | ||||||
|  | 				<p>"Gitea {{.RemoteVersion | Str2html}} is now available, you are running {{.AppVer | Str2html}}. Check the <a href="https://blog.gitea.io">blog</a> for more details.</p> | ||||||
|  | 			</div> | ||||||
|  | 		{{end}} | ||||||
| 		<h4 class="ui top attached header"> | 		<h4 class="ui top attached header"> | ||||||
| 			{{.i18n.Tr "admin.dashboard.statistic"}} | 			{{.i18n.Tr "admin.dashboard.statistic"}} | ||||||
| 		</h4> | 		</h4> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue