Skip to content

Commit

Permalink
Merge pull request #609 from yast/release-notes-from-packages
Browse files Browse the repository at this point in the history
Release notes from packages
  • Loading branch information
imobachgs committed Sep 26, 2017
2 parents ad6788d + 5af425b commit 819f500
Show file tree
Hide file tree
Showing 12 changed files with 251 additions and 621 deletions.
2 changes: 1 addition & 1 deletion doc/SELF_UPDATE.md
@@ -1,6 +1,6 @@
# Installer Self-update

Starting on version 3.1.175, yast2-install is able to update itself during
Starting on version 3.1.175, yast2-installation is able to update itself during
system installation. This feature will help to solve problems with the
installation even after the media has been released. Check
[FATE#319716](https://fate.suse.com/319716) for a more in-depth rationale.
Expand Down
25 changes: 25 additions & 0 deletions doc/release_notes.md
@@ -0,0 +1,25 @@
# Release notes

Starting on version 4.0.3, yast2-installation will read release notes from RPM
packages ([FATE#323273](https://fate.suse.com/323273)) relying on the new API
introduced in yast2-packager 4.0.5.

As a consequence, YaST2 will need to know which package contains release notes
for a given product. This relationship will be defined in the release notes package
specification using the tag `release-notes()`:

Provides: release-notes() = openSUSE

The package should contain a set of files with the following name:
`RELEASE-NOTES.[lang].[format]`, where `lang` and `format` should be replaced by
language code and format. For instance:

* `RELEASE-NOTES.en_US.txt` English version of release notes for textmode interface.
* `RELEASE-NOTES.de_DE.rtf` German version of release notes for graphical interface.
* `RELEASE-NOTES.es.rtf` Spanish version of release notes for graphical
interface. Note that it is possible to use a two characters language code
which will be used as fallback for `es_ES`, `es_AR`, etc.

Those files could be placed under any directory, although they usually will
live under `/usr/share/doc/release-notes/[product]/`. For instance,
`/usr/share/doc/release-notes/openSUSE`.
12 changes: 12 additions & 0 deletions package/yast2-installation.changes
@@ -1,3 +1,15 @@
-------------------------------------------------------------------
Tue Sep 26 13:37:17 UTC 2017 - igonzalezsosa@suse.com

- Add support to read release notes from an RPM in the
repository (fate#323273)
- 4.0.4

-------------------------------------------------------------------
Tue Sep 26 13:35:36 UTC 2017 - jreidinger@suse.com

- Add support for system roles ordering (related to bsc#1049297)

-------------------------------------------------------------------
Tue Sep 26 08:50:31 UTC 2017 - igonzalezsosa@suse.com

Expand Down
8 changes: 5 additions & 3 deletions package/yast2-installation.spec
Expand Up @@ -17,7 +17,7 @@


Name: yast2-installation
Version: 4.0.3
Version: 4.0.4
Release: 0

BuildRoot: %{_tmppath}/%{name}-%{version}-build
Expand All @@ -43,6 +43,8 @@ BuildRequires: rubygem(yast-rake)

# Yast::WorkflowManager.merge_modules_extensions
BuildRequires: yast2 >= 4.0.8
# Y2Packager::Product#release_notes Y2Packager::ReleaseNotes
BuildRequires: yast2-packager >= 4.0.5

# New Y2Storage::StorageManager API
BuildRequires: yast2-storage-ng >= 0.1.32
Expand All @@ -64,8 +66,8 @@ Requires: yast2-pkg-bindings >= 3.1.33
# Mouse-related scripts moved to yast2-mouse
Conflicts: yast2-mouse < 2.18.0

# Y2Packager::Widgets::ProductLicense with skip validation option
Requires: yast2-packager >= 3.3.10
# Y2Packager::Product#release_notes Y2Packager::ReleaseNotes
Requires: yast2-packager >= 4.0.5

# FIXME: some code present in this package still depends on the old yast2-storage
# and will break without this dependency. That's acceptable at this point of the
Expand Down
8 changes: 0 additions & 8 deletions src/lib/installation/clients/inst_complex_welcome.rb
Expand Up @@ -28,7 +28,6 @@
Yast.import "Console"
Yast.import "FileUtils"
Yast.import "GetInstArgs"
Yast.import "InstData"
Yast.import "InstShowInfo"
Yast.import "Keyboard"
Yast.import "Language"
Expand Down Expand Up @@ -62,7 +61,6 @@ def main
textdomain "installation"

Yast::Wizard.EnableAbortButton
show_release_notes

loop do
dialog_result = ::Installation::Dialogs::ComplexWelcome.run(
Expand Down Expand Up @@ -144,12 +142,6 @@ def setup_final_choice
log.info "Language: '#{Language.language}', system encoding '#{WFM.GetEncoding}'"
end

# Show release notes if they have been downloaded
def show_release_notes
return if InstData.release_notes.empty?
Wizard.ShowReleaseNotesButton(_("Re&lease Notes..."), "rel_notes")
end

# Return the list of base products
#
# @return [Array<Y2Packager::Product>] List of available base products
Expand Down
233 changes: 44 additions & 189 deletions src/lib/installation/clients/inst_download_release_notes.rb
Expand Up @@ -25,186 +25,44 @@
# Purpose: Downloads on-line release notes
#
# $Id$
module Yast
class InstDownloadReleaseNotesClient < Client
include Yast::Logger

# When cURL returns one of those codes, the download won't be retried
# @see man curl
CURL_GIVE_UP_RETURN_CODES = {
5 => "Couldn't resolve proxy.",
6 => "Couldn't resolve host.",
7 => "Failed to connect to host.",
28 => "Operation timeout."
}.freeze

# Download *url* to *filename*
# May set InstData.stop_relnotes_download on download failure.
#
# @return [Boolean,nil] true: success, false: failure, nil: failure+dont retry
def curl_download(url, filename, proxy_args:, max_time: 300)
cmd = Builtins.sformat(
"/usr/bin/curl --location --verbose --fail --max-time %6 --connect-timeout 15 %1 '%2' --output '%3' > '%4/%5' 2>&1",
proxy_args,
url,
String.Quote(filename),
String.Quote(Directory.logdir),
"curl_log",
max_time
)
ret = SCR.Execute(path(".target.bash"), cmd)
log.info("#{cmd} returned #{ret}")
reason = CURL_GIVE_UP_RETURN_CODES[ret]
if !reason.nil?
log.info "Communication with server failed (#{reason}), skipping further attempts."
InstData.stop_relnotes_download = true
return nil
end
ret == 0
end

# @return [String] to be interpolated in a .target.bash command, unquoted
def curl_proxy_args
proxy = ""
# proxy should be set by inst_install_inf if set via Linuxrc
Proxy.Read
# Test if proxy works
if Proxy.enabled
# it is enough to test http proxy, release notes are downloaded via http
proxy_ret = Proxy.RunTestProxy(
Proxy.http,
"",
"",
Proxy.user,
Proxy.pass
)

if Ops.get_boolean(proxy_ret, ["HTTP", "tested"], true) == true &&
Ops.get_integer(proxy_ret, ["HTTP", "exit"], 1) == 0
user_pass = Proxy.user != "" ? "#{Proxy.user}:#{Proxy.pass}" : ""
proxy = "--proxy #{Proxy.http}"
proxy << " --proxy-user '#{user_pass}'" unless user_pass.empty?
end
end
proxy
end
require "y2packager/product"

# Download of index of release notes for a specific product
# @param url_base URL pointing to directory with the index
# @param proxy the proxy URL to be passed to curl
#
# May set InstData.stop_relnotes_download on download failure.
# @return [Array<String>,nil] filenames, nil if not found
def download_release_notes_index(url_base, proxy)
url_index = url_base + "/directory.yast"
log.info("Index with available files: #{url_index}")
filename = Builtins.sformat("%1/directory.yast", SCR.Read(path(".target.tmpdir")))
# download the index with much shorter time-out
ok = curl_download(url_index, filename, proxy_args: proxy, max_time: 30)
Yast.import "InstData"
Yast.import "Pkg"
Yast.import "Packages"
Yast.import "Stage"
Yast.import "UI"
Yast.import "GetInstArgs"
Yast.import "Wizard"
Yast.import "Mode"

if ok
log.info("Release notes index downloaded successfully")
index_file = File.read(filename)
if index_file.nil? || index_file.empty?
log.info("Release notes index empty, not filtering further downloads")
return nil
else
rn_filter = index_file.split("\n")
log.info("Index of RN files at the server: #{rn_filter}")
return rn_filter
end
elsif ok.nil?
return nil
else
log.info "Downloading index failed, trying all files according to selected language"
return nil
end
end
module Yast
# Client to download and manage release notes button
#
# This client ask for products' release notes and sets UI elements accordingly
# ("Release Notes" button and dialog).
class InstDownloadReleaseNotesClient < Client
include Yast::Logger

# Download release notes for all selected and installed products
#
# @return true when successful
def download_release_notes
filename_templ = UI.TextMode ? "/RELEASE-NOTES.%1.txt" : "/RELEASE-NOTES.%1.rtf"
format = UI.TextMode ? :txt : :rtf

# Get proxy settings (if any)
proxy = curl_proxy_args

required_product_statuses = check_product_states
log.info("Checking products in state: #{required_product_statuses}")
products = Pkg.ResolvableProperties("", :product, "").select do |product|
required_product_statuses.include? product["status"]
end
log.info("Products: #{products}")
products.each do |product|
if InstData.stop_relnotes_download
log.info("Skipping release notes download due to previous download issues")
break
end
if InstData.downloaded_release_notes.include? product["short_name"]
log.info("Release notes for #{product["short_name"]} already downloaded, skipping...")
next
end
url = product["relnotes_url"]
log.debug("URL: #{url}")
# protect from wrong urls
if url.nil? || url.empty?
log.warn("Skipping invalid URL #{url.inspect} for product #{product["short_name"]}")
relnotes_map = products.each_with_object({}) do |product, all|
relnotes = product.release_notes(format)
if relnotes.nil?
log.info "No release notes were found for product #{product.short_name}"
next
end
pos = url.rindex("/")
if pos.nil?
log.error "Broken URL for release notes: #{url}"
next
end
url_base = url[0, pos]

rn_filter = download_release_notes_index(url_base, proxy)
if InstData.stop_relnotes_download
log.info("Skipping release notes download due to previous download issues")
break
end

url_template = url_base + filename_templ
log.info("URL template: #{url_base}")
[Language.language, Language.language[0..1], "en"].uniq.each do |lang|
if !rn_filter.nil?
filename = Builtins.sformat(filename_templ, lang)
if !rn_filter.include?(filename[1..-1])
log.info "File #{filename} not found in index, skipping attempt download"
next
end
end
url = Builtins.sformat(url_template, lang)
log.info("URL: #{url}")
# Where we want to store the downloaded release notes
filename = Builtins.sformat("%1/relnotes",
SCR.Read(path(".target.tmpdir")))

if InstData.failed_release_notes.include?(url)
log.info("Skipping download of already failed release notes at #{url}")
next
end

# download release notes now
ok = curl_download(url, filename, proxy_args: proxy)
if ok
log.info("Release notes downloaded successfully")
InstData.release_notes[product["short_name"]] = SCR.Read(path(".target.string"), filename)
InstData.downloaded_release_notes << product["short_name"]
break
elsif ok.nil?
break
else
InstData.failed_release_notes << url
end
end
all[product.short_name] = relnotes.content
end
if !InstData.release_notes.empty?
UI.SetReleaseNotes(InstData.release_notes)
Wizard.ShowReleaseNotesButton(_("Re&lease Notes..."), "rel_notes")
end
true

refresh_ui(relnotes_map)
InstData.release_notes = relnotes_map
!relnotes_map.empty?
end

# Set the UI content to show some progress.
Expand All @@ -215,18 +73,10 @@ def init_ui
end

def main
Yast.import "UI"
Yast.import "Language"
Yast.import "Proxy"
Yast.import "Directory"
Yast.import "InstData"
Yast.import "Stage"
Yast.import "GetInstArgs"
Yast.import "Wizard"
Yast.import "Mode"

textdomain "installation"

return :auto unless Packages.init_called

return :back if GetInstArgs.going_back

# skip download during AutoYaST
Expand All @@ -243,21 +93,26 @@ def main

private

# Get the list of product states which should be used for downloading
# release notes.
# @return [Array<Symbol>] list of states (:selected, :installed or :available)
def check_product_states
# List of products which should be used for downloading release notes
#
# @return [Array<Y2Packager::Product>] list of products
def products
# installed may mean old (before upgrade) in initial stage
# product may not yet be selected although repo is already added
return [:selected, :installed] unless Stage.initial
return Y2Packager::Product.with_status(:selected, :installed) unless Stage.initial
selected = Y2Packager::Product.with_status(:selected)
return selected unless selected.empty?
Y2Packager::Product.with_status(:available)
end

# if a product is already selected then use the selected products
# otherwise use the available one(s)
product_selected = Pkg.ResolvableProperties("", :product, "").any? do |p|
p["status"] == :selected
# Refresh release notes UI
def refresh_ui(relnotes_map)
UI.SetReleaseNotes(relnotes_map)
if relnotes_map.empty?
Wizard.HideReleaseNotesButton
else
Wizard.ShowReleaseNotesButton(_("Re&lease Notes..."), "rel_notes")
end

product_selected ? [:selected] : [:available]
end
end
end

0 comments on commit 819f500

Please sign in to comment.