mirror of
https://github.com/zulip/zulip.git
synced 2025-11-21 15:09:34 +00:00
apps: Use GitHub API for generating the web app download link.
This commit is contained in:
@@ -22,33 +22,13 @@ const hello_events = function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const apps_events = function () {
|
const apps_events = function () {
|
||||||
const ELECTRON_APP_VERSION = page_params.electron_app_version;
|
|
||||||
const ELECTRON_APP_URL_LINUX =
|
|
||||||
"https://github.com/zulip/zulip-desktop/releases/download/v" +
|
|
||||||
ELECTRON_APP_VERSION +
|
|
||||||
"/Zulip-" +
|
|
||||||
ELECTRON_APP_VERSION +
|
|
||||||
"-x86_64.AppImage";
|
|
||||||
const ELECTRON_APP_URL_MAC =
|
|
||||||
"https://github.com/zulip/zulip-desktop/releases/download/v" +
|
|
||||||
ELECTRON_APP_VERSION +
|
|
||||||
"/Zulip-" +
|
|
||||||
ELECTRON_APP_VERSION +
|
|
||||||
".dmg";
|
|
||||||
const ELECTRON_APP_URL_WINDOWS =
|
|
||||||
"https://github.com/zulip/zulip-desktop/releases/download/v" +
|
|
||||||
ELECTRON_APP_VERSION +
|
|
||||||
"/Zulip-Web-Setup-" +
|
|
||||||
ELECTRON_APP_VERSION +
|
|
||||||
".exe";
|
|
||||||
|
|
||||||
const info = {
|
const info = {
|
||||||
windows: {
|
windows: {
|
||||||
image: "/static/images/landing-page/microsoft.png",
|
image: "/static/images/landing-page/microsoft.png",
|
||||||
alt: "Windows",
|
alt: "Windows",
|
||||||
description:
|
description:
|
||||||
"Zulip for Windows is even better than Zulip on the web, with a cleaner look, tray integration, native notifications, and support for multiple Zulip accounts.",
|
"Zulip for Windows is even better than Zulip on the web, with a cleaner look, tray integration, native notifications, and support for multiple Zulip accounts.",
|
||||||
link: ELECTRON_APP_URL_WINDOWS,
|
download_link: "/apps/download/windows",
|
||||||
show_instructions: true,
|
show_instructions: true,
|
||||||
install_guide: "/help/desktop-app-install-guide",
|
install_guide: "/help/desktop-app-install-guide",
|
||||||
app_type: "desktop",
|
app_type: "desktop",
|
||||||
@@ -58,7 +38,7 @@ const apps_events = function () {
|
|||||||
alt: "macOS",
|
alt: "macOS",
|
||||||
description:
|
description:
|
||||||
"Zulip on macOS is even better than Zulip on the web, with a cleaner look, tray integration, native notifications, and support for multiple Zulip accounts.",
|
"Zulip on macOS is even better than Zulip on the web, with a cleaner look, tray integration, native notifications, and support for multiple Zulip accounts.",
|
||||||
link: ELECTRON_APP_URL_MAC,
|
download_link: "/apps/download/mac",
|
||||||
show_instructions: true,
|
show_instructions: true,
|
||||||
install_guide: "/help/desktop-app-install-guide",
|
install_guide: "/help/desktop-app-install-guide",
|
||||||
app_type: "desktop",
|
app_type: "desktop",
|
||||||
@@ -84,7 +64,7 @@ const apps_events = function () {
|
|||||||
alt: "Linux",
|
alt: "Linux",
|
||||||
description:
|
description:
|
||||||
"Zulip on the Linux desktop is even better than Zulip on the web, with a cleaner look, tray integration, native notifications, and support for multiple Zulip accounts.",
|
"Zulip on the Linux desktop is even better than Zulip on the web, with a cleaner look, tray integration, native notifications, and support for multiple Zulip accounts.",
|
||||||
link: ELECTRON_APP_URL_LINUX,
|
download_link: "/apps/download/linux",
|
||||||
show_instructions: true,
|
show_instructions: true,
|
||||||
install_guide: "/help/desktop-app-install-guide",
|
install_guide: "/help/desktop-app-install-guide",
|
||||||
app_type: "desktop",
|
app_type: "desktop",
|
||||||
@@ -127,7 +107,7 @@ const apps_events = function () {
|
|||||||
|
|
||||||
$(".info .platform").text(version_info.alt);
|
$(".info .platform").text(version_info.alt);
|
||||||
$(".info .description").text(version_info.description);
|
$(".info .description").text(version_info.description);
|
||||||
$(".info .desktop-download-link").attr("href", version_info.link);
|
$(".info .desktop-download-link").attr("href", version_info.download_link);
|
||||||
$(".download-from-google-play-store").attr("href", version_info.link);
|
$(".download-from-google-play-store").attr("href", version_info.link);
|
||||||
$(".download-from-apple-app-store").attr("href", version_info.link);
|
$(".download-from-apple-app-store").attr("href", version_info.link);
|
||||||
$(".image img").attr("src", version_info.image);
|
$(".image img").attr("src", version_info.image);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ if os.path.exists(zulip_git_version_file):
|
|||||||
LATEST_MAJOR_VERSION = "3.0"
|
LATEST_MAJOR_VERSION = "3.0"
|
||||||
LATEST_RELEASE_VERSION = "3.0"
|
LATEST_RELEASE_VERSION = "3.0"
|
||||||
LATEST_RELEASE_ANNOUNCEMENT = "https://blog.zulip.org/2020/07/16/zulip-3-0-released/"
|
LATEST_RELEASE_ANNOUNCEMENT = "https://blog.zulip.org/2020/07/16/zulip-3-0-released/"
|
||||||
LATEST_DESKTOP_VERSION = "5.4.3"
|
|
||||||
|
|
||||||
# Versions of the desktop app below DESKTOP_MINIMUM_VERSION will be
|
# Versions of the desktop app below DESKTOP_MINIMUM_VERSION will be
|
||||||
# prevented from connecting to the Zulip server. Versions above
|
# prevented from connecting to the Zulip server. Versions above
|
||||||
|
|||||||
48
zerver/lib/github.py
Normal file
48
zerver/lib/github.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import json
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from zerver.lib.cache import cache_with_key
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
def get_latest_github_release_version_for_repo(repo: str) -> str:
|
||||||
|
api_url = f"https://api.github.com/repos/zulip/{repo}/releases/latest"
|
||||||
|
try:
|
||||||
|
return requests.get(api_url).json()["tag_name"]
|
||||||
|
except (requests.RequestException, json.JSONDecodeError, KeyError):
|
||||||
|
logger.error("Unable to fetch the latest release version from GitHub %s", api_url)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def verify_release_download_link(link: str) -> bool:
|
||||||
|
try:
|
||||||
|
requests.head(link).raise_for_status()
|
||||||
|
return True
|
||||||
|
except requests.RequestException:
|
||||||
|
logger.error("App download link is broken %s", link)
|
||||||
|
return False
|
||||||
|
|
||||||
|
PLATFORM_TO_SETUP_FILE = {
|
||||||
|
"linux": "Zulip-{version}-x86_64.AppImage",
|
||||||
|
"mac": "Zulip-{version}.dmg",
|
||||||
|
"windows": "Zulip-Web-Setup-{version}.exe",
|
||||||
|
}
|
||||||
|
|
||||||
|
class InvalidPlatform(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@cache_with_key(lambda platform: f"download_link:{platform}", timeout=60*30)
|
||||||
|
def get_latest_github_release_download_link_for_platform(platform: str) -> str:
|
||||||
|
if platform not in PLATFORM_TO_SETUP_FILE:
|
||||||
|
raise InvalidPlatform()
|
||||||
|
|
||||||
|
latest_version = get_latest_github_release_version_for_repo("zulip-desktop")
|
||||||
|
if latest_version:
|
||||||
|
if latest_version[0] in ["v", "V"]:
|
||||||
|
latest_version = latest_version[1:]
|
||||||
|
setup_file = PLATFORM_TO_SETUP_FILE[platform].format(version=latest_version)
|
||||||
|
link = f"https://github.com/zulip/zulip-desktop/releases/download/v{latest_version}/{setup_file}"
|
||||||
|
if verify_release_download_link(link):
|
||||||
|
return link
|
||||||
|
return "https://github.com/zulip/zulip-desktop/releases/latest"
|
||||||
@@ -471,6 +471,17 @@ class AppsPageTest(ZulipTestCase):
|
|||||||
html = result.content.decode('utf-8')
|
html = result.content.decode('utf-8')
|
||||||
self.assertIn('Apps for every platform.', html)
|
self.assertIn('Apps for every platform.', html)
|
||||||
|
|
||||||
|
def test_app_download_link_view(self) -> None:
|
||||||
|
return_value = "https://github.com/zulip/zulip-desktop/releases/download/v5.4.3/Zulip-Web-Setup-5.4.3.exe"
|
||||||
|
with mock.patch("zerver.views.portico.get_latest_github_release_download_link_for_platform", return_value=return_value) as m:
|
||||||
|
result = self.client_get("/apps/download/windows")
|
||||||
|
m.assert_called_once_with("windows")
|
||||||
|
self.assertEqual(result.status_code, 302)
|
||||||
|
self.assertTrue(result['Location'] == return_value)
|
||||||
|
|
||||||
|
result = self.client_get("/apps/download/plan9")
|
||||||
|
self.assertEqual(result.status_code, 404)
|
||||||
|
|
||||||
class PrivacyTermsTest(ZulipTestCase):
|
class PrivacyTermsTest(ZulipTestCase):
|
||||||
def test_custom_tos_template(self) -> None:
|
def test_custom_tos_template(self) -> None:
|
||||||
response = self.client_get("/terms/")
|
response = self.client_get("/terms/")
|
||||||
|
|||||||
64
zerver/tests/test_github.py
Normal file
64
zerver/tests/test_github.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import requests
|
||||||
|
import responses
|
||||||
|
|
||||||
|
from zerver.lib.cache import cache_delete
|
||||||
|
from zerver.lib.github import InvalidPlatform, get_latest_github_release_download_link_for_platform
|
||||||
|
from zerver.lib.test_classes import ZulipTestCase
|
||||||
|
|
||||||
|
logger_string = "zerver.lib.github"
|
||||||
|
|
||||||
|
class GitHubTestCase(ZulipTestCase):
|
||||||
|
@responses.activate
|
||||||
|
def test_get_latest_github_release_download_link_for_platform(self) -> None:
|
||||||
|
responses.add(responses.GET, "https://api.github.com/repos/zulip/zulip-desktop/releases/latest",
|
||||||
|
json={"tag_name": "v5.4.3"}, status=200)
|
||||||
|
|
||||||
|
responses.add(responses.HEAD, "https://github.com/zulip/zulip-desktop/releases/download/v5.4.3/Zulip-Web-Setup-5.4.3.exe",
|
||||||
|
status=302)
|
||||||
|
self.assertEqual(
|
||||||
|
get_latest_github_release_download_link_for_platform("windows"),
|
||||||
|
"https://github.com/zulip/zulip-desktop/releases/download/v5.4.3/Zulip-Web-Setup-5.4.3.exe"
|
||||||
|
)
|
||||||
|
|
||||||
|
responses.add(responses.HEAD, "https://github.com/zulip/zulip-desktop/releases/download/v5.4.3/Zulip-5.4.3-x86_64.AppImage",
|
||||||
|
status=302)
|
||||||
|
self.assertEqual(
|
||||||
|
get_latest_github_release_download_link_for_platform("linux"),
|
||||||
|
"https://github.com/zulip/zulip-desktop/releases/download/v5.4.3/Zulip-5.4.3-x86_64.AppImage"
|
||||||
|
)
|
||||||
|
|
||||||
|
responses.add(responses.HEAD, "https://github.com/zulip/zulip-desktop/releases/download/v5.4.3/Zulip-5.4.3.dmg", status=302)
|
||||||
|
self.assertEqual(
|
||||||
|
get_latest_github_release_download_link_for_platform("mac"),
|
||||||
|
"https://github.com/zulip/zulip-desktop/releases/download/v5.4.3/Zulip-5.4.3.dmg"
|
||||||
|
)
|
||||||
|
|
||||||
|
api_url = "https://api.github.com/repos/zulip/zulip-desktop/releases/latest"
|
||||||
|
responses.replace(responses.GET, api_url, body=requests.RequestException())
|
||||||
|
cache_delete("download_link:windows")
|
||||||
|
with self.assertLogs(logger_string, level='ERROR') as error_log:
|
||||||
|
self.assertEqual(
|
||||||
|
get_latest_github_release_download_link_for_platform("windows"),
|
||||||
|
"https://github.com/zulip/zulip-desktop/releases/latest"
|
||||||
|
)
|
||||||
|
self.assertEqual(error_log.output, [
|
||||||
|
f'ERROR:{logger_string}:Unable to fetch the latest release version from GitHub {api_url}'
|
||||||
|
])
|
||||||
|
|
||||||
|
responses.replace(responses.GET, "https://api.github.com/repos/zulip/zulip-desktop/releases/latest",
|
||||||
|
json={"tag_name": "5.4.4"}, status=200)
|
||||||
|
download_link = "https://github.com/zulip/zulip-desktop/releases/download/v5.4.4/Zulip-5.4.4-x86_64.AppImage"
|
||||||
|
responses.add(responses.HEAD, download_link, status=404)
|
||||||
|
cache_delete("download_link:linux")
|
||||||
|
with self.assertLogs(logger_string, level='ERROR') as error_log:
|
||||||
|
self.assertEqual(
|
||||||
|
get_latest_github_release_download_link_for_platform("linux"),
|
||||||
|
"https://github.com/zulip/zulip-desktop/releases/latest"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(error_log.output, [
|
||||||
|
f'ERROR:{logger_string}:App download link is broken {download_link}'
|
||||||
|
])
|
||||||
|
|
||||||
|
with self.assertRaises(InvalidPlatform):
|
||||||
|
get_latest_github_release_download_link_for_platform("plan9")
|
||||||
@@ -6,9 +6,9 @@ from django.contrib.auth.views import redirect_to_login
|
|||||||
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
||||||
from django.template.response import TemplateResponse
|
from django.template.response import TemplateResponse
|
||||||
|
|
||||||
from version import LATEST_DESKTOP_VERSION
|
|
||||||
from zerver.context_processors import get_realm_from_request, latest_info_context
|
from zerver.context_processors import get_realm_from_request, latest_info_context
|
||||||
from zerver.decorator import add_google_analytics
|
from zerver.decorator import add_google_analytics
|
||||||
|
from zerver.lib.github import InvalidPlatform, get_latest_github_release_download_link_for_platform
|
||||||
from zerver.models import Realm
|
from zerver.models import Realm
|
||||||
|
|
||||||
|
|
||||||
@@ -18,14 +18,16 @@ def apps_view(request: HttpRequest, platform: Optional[str] = None) -> HttpRespo
|
|||||||
return TemplateResponse(
|
return TemplateResponse(
|
||||||
request,
|
request,
|
||||||
'zerver/apps.html',
|
'zerver/apps.html',
|
||||||
context={
|
|
||||||
"page_params": {
|
|
||||||
'electron_app_version': LATEST_DESKTOP_VERSION,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect('https://zulip.com/apps/', status=301)
|
return HttpResponseRedirect('https://zulip.com/apps/', status=301)
|
||||||
|
|
||||||
|
def app_download_link_redirect(request: HttpRequest, platform: str) -> HttpResponse:
|
||||||
|
try:
|
||||||
|
download_link = get_latest_github_release_download_link_for_platform(platform)
|
||||||
|
return HttpResponseRedirect(download_link, status=302)
|
||||||
|
except InvalidPlatform:
|
||||||
|
return TemplateResponse(request, "404.html", status=404)
|
||||||
|
|
||||||
@add_google_analytics
|
@add_google_analytics
|
||||||
def plans_view(request: HttpRequest) -> HttpResponse:
|
def plans_view(request: HttpRequest) -> HttpResponse:
|
||||||
realm = get_realm_from_request(request)
|
realm = get_realm_from_request(request)
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ from zerver.views.message_flags import (
|
|||||||
from zerver.views.message_send import render_message_backend, send_message_backend, zcommand_backend
|
from zerver.views.message_send import render_message_backend, send_message_backend, zcommand_backend
|
||||||
from zerver.views.muting import update_muted_topic
|
from zerver.views.muting import update_muted_topic
|
||||||
from zerver.views.portico import (
|
from zerver.views.portico import (
|
||||||
|
app_download_link_redirect,
|
||||||
apps_view,
|
apps_view,
|
||||||
hello_view,
|
hello_view,
|
||||||
landing_view,
|
landing_view,
|
||||||
@@ -728,6 +729,7 @@ i18n_urls = [
|
|||||||
path('features/', landing_view, {'template_name': 'zerver/features.html'}),
|
path('features/', landing_view, {'template_name': 'zerver/features.html'}),
|
||||||
path('plans/', plans_view, name='plans'),
|
path('plans/', plans_view, name='plans'),
|
||||||
path('apps/', apps_view),
|
path('apps/', apps_view),
|
||||||
|
path('apps/download/<platform>', app_download_link_redirect),
|
||||||
path('apps/<platform>', apps_view),
|
path('apps/<platform>', apps_view),
|
||||||
path('team/', team_view),
|
path('team/', team_view),
|
||||||
path('history/', landing_view, {'template_name': 'zerver/history.html'}),
|
path('history/', landing_view, {'template_name': 'zerver/history.html'}),
|
||||||
|
|||||||
Reference in New Issue
Block a user