0
0
mirror of https://github.com/Doodle3D/doodle3d-firmware.git synced 2024-12-22 11:03:48 +01:00

Add a number of sanity checks to release script.

Tidying up.
Several changes and fixes to beta updating code.
This commit is contained in:
Wouter R 2014-02-24 15:22:08 +01:00
parent 264511f809
commit 524ef027f5
4 changed files with 93 additions and 34 deletions

View File

@ -32,6 +32,7 @@ local IMAGE_BASENAME = 'doodle3d-wifibox'
local BACKUP_FILE_SUFFIX = 'bkp' local BACKUP_FILE_SUFFIX = 'bkp'
local RELEASE_NOTES_FILE = "ReleaseNotes.md" local RELEASE_NOTES_FILE = "ReleaseNotes.md"
local RSYNC_TIMEOUT = 2 local RSYNC_TIMEOUT = 2
local MAX_VIABLE_IMAGE_SIZE = 3500000
local deviceType = 'tl-mr3020' -- or 'tl-wr703' local deviceType = 'tl-mr3020' -- or 'tl-wr703'
local lock = nil local lock = nil
@ -80,6 +81,14 @@ local function detectRootPrivileges()
return tonumber(userId) == 0 and true or false return tonumber(userId) == 0 and true or false
end end
local function findInFile(needle, file)
local f = io.open(file, 'r')
if not f then return nil,"could not open file" end
local t = f:read('*all')
return not not t:find(needle, 1, true)
end
local function detectOpenWrtRoot() local function detectOpenWrtRoot()
local f = io.open('Makefile', 'r') local f = io.open('Makefile', 'r')
local line = f and f:read('*line') local line = f and f:read('*line')
@ -133,7 +142,9 @@ local function runAction(actMsg, errMsg, ev, func)
io.stdout:write("* " .. actMsg .. "...") io.stdout:write("* " .. actMsg .. "...")
local rv,err = func() local rv,err = func()
if not rv then if not rv then
print("Error: " .. errMsg .. " (" .. err .. ")") if err then print("Error: " .. errMsg .. " (" .. err .. ")")
else print("Error: " .. errMsg)
end
quit(ev) quit(ev)
else else
print("ok") print("ok")
@ -178,7 +189,7 @@ local function prepare()
local isOpenWrtRoot = detectOpenWrtRoot() local isOpenWrtRoot = detectOpenWrtRoot()
if isOpenWrtRoot then if isOpenWrtRoot then
paths.wrt = pl.path.currentdir() paths.wrt = pl.path.currentdir()
print("found (" .. paths.wrt .. ")") print("found " .. paths.wrt)
else else
print("unrecognized directory, try changing directories or using -wrt-root") print("unrecognized directory, try changing directories or using -wrt-root")
return nil return nil
@ -199,6 +210,7 @@ local function prepare()
-- if empty, try to choose something sensible -- if empty, try to choose something sensible
if not paths.cache or paths.cache == '' then if not paths.cache or paths.cache == '' then
--paths.cache = pl.app.appfile('')
paths.cache = '/tmp/d3d-release-dir' paths.cache = '/tmp/d3d-release-dir'
end end
io.stdout:write("* Attempting to use " .. paths.cache .. " as cache dir... ") io.stdout:write("* Attempting to use " .. paths.cache .. " as cache dir... ")
@ -268,6 +280,7 @@ end
local function generateIndex(newVersion, versionTable, isStable) local function generateIndex(newVersion, versionTable, isStable)
local indexFilename = isStable and um.IMAGE_STABLE_INDEX_FILE or um.IMAGE_BETA_INDEX_FILE local indexFilename = isStable and um.IMAGE_STABLE_INDEX_FILE or um.IMAGE_BETA_INDEX_FILE
versionTable[#versionTable+1] = newVersion
local sortedVers = pl.List(versionTable) local sortedVers = pl.List(versionTable)
sortedVers:sort(function(a, b) sortedVers:sort(function(a, b)
return um.compareVersions(a.version, b.version, a.timestamp, b.timestamp) < 0 return um.compareVersions(a.version, b.version, a.timestamp, b.timestamp) < 0
@ -303,14 +316,20 @@ local function copyImages(newVersion)
return true return true
end end
local function copyReleaseNotes() local function copyReleaseNotes(newVersion)
local releaseNotesPath = pl.path.join(imageCachePath(), RELEASE_NOTES_FILE) local srcReleaseNotesPath = pl.path.join(paths.firmware, RELEASE_NOTES_FILE)
if pl.path.isfile(releaseNotesPath) then local tgtReleaseNotesPath = pl.path.join(imageCachePath(), RELEASE_NOTES_FILE)
local rv = pl.file.copy(releaseNotesPath, pl.path.join(paths.cache, RELEASE_NOTES_FILE..'.'..BACKUP_FILE_SUFFIX))
if not findInFile(um.formatVersion(newVersion.version), srcReleaseNotesPath) then
return nil,"version not mentioned in release notes file"
end
if pl.path.isfile(tgtReleaseNotesPath) then
local rv = pl.file.copy(tgtReleaseNotesPath, tgtReleaseNotesPath..'.'..BACKUP_FILE_SUFFIX)
if not rv then return nil,"could not backup file" end if not rv then return nil,"could not backup file" end
end end
local rv = pl.file.copy(pl.path.join(paths.firmware, RELEASE_NOTES_FILE), releaseNotesPath) local rv = pl.file.copy(srcReleaseNotesPath, tgtReleaseNotesPath)
if not rv then return nil,"could not copy file" end if not rv then return nil,"could not copy file" end
return true return true
@ -333,6 +352,26 @@ local function uploadFiles()
return rv and true or nil,("rsync failed, exit status: " .. ev) return rv and true or nil,("rsync failed, exit status: " .. ev)
end end
local function checkWrtConfig()
local goodConfigPath = pl.path.join(paths.firmware, "extra/openwrt-build/openwrt-diffconfig-extramini")
local wrtConfigPath = pl.path.tmpname()
--print("diffonfig output file: " .. wrtConfigPath)
local rv,ev = pl.utils.execute('./scripts/diffconfig.sh > "' .. wrtConfigPath .. '"')
if not rv then return nil,"could not run diffconfig script (exit status: " .. ev .. ")" end
local _,rv,output = pl.utils.executeex('diff "' .. wrtConfigPath .. '" "' .. goodConfigPath .. '"')
if rv == 0 then
return true
elseif rv == 1 then
print("configurations differ:\n-----------------------\n" .. output .. "\n-----------------------")
--ask for confirmation?
else
return nil,"unexpected exit status from diff (" .. rv .. ")"
end
end
local function main() local function main()
print("\nDoodle3D release script") print("\nDoodle3D release script")
if detectRootPrivileges() then if detectRootPrivileges() then
@ -359,19 +398,13 @@ local function main()
quit(3) quit(3)
end end
local isStable = (newVersion.version.suffix == nil)
print("\nRolling release for firmware version " .. um.formatVersion(newVersion.version) .. " (type: " .. (isStable and "stable" or "beta") .. ").")
local stables,betas = fetchVersionInfo() local stables,betas = fetchVersionInfo()
if not stables then if not stables then
print("Error: could not get version information (" .. betas .. ")") print("Error: could not get version information (" .. betas .. ")")
quit(1) quit(1)
end end
if um.findVersion(newVersion.version, nil, stables) or um.findVersion(newVersion.version, nil, betas) then --TODO: if requested, fetch images and packages (i.e., mirror whole directory)
print("Error: firmware version " .. um.formatVersion(newVersion.version) .. " already exists")
quit(3)
end
-- pl.pretty.dump(newVersion) -- pl.pretty.dump(newVersion)
@ -379,23 +412,41 @@ local function main()
-- print("==========================="); -- print("===========================");
-- print("betas: "); pl.pretty.dump(betas) -- print("betas: "); pl.pretty.dump(betas)
--TODO: if requested, fetch images and packages (i.e., mirror whole directory)
--TODO: run sanity checks
print("\nRunning sanity checks")
runAction("Generating new index file", "could not generate index", 4, function() runAction("Checking whether version is unique",
"firmware version " .. um.formatVersion(newVersion.version) .. " already exists", 3, function()
return not (um.findVersion(newVersion.version, nil, stables) or um.findVersion(newVersion.version, nil, betas)) and true or nil
end)
runAction("Checking OpenWrt config", "failed", 3, checkWrtConfig)
--TODO: check git repos (`git log -n 1 --pretty=format:%ct` gives commit date of last commit (not author date))
local isStable = (newVersion.version.suffix == nil)
print("\nRolling release for firmware version " .. um.formatVersion(newVersion.version) .. " (type: " .. (isStable and "stable" or "beta") .. ").")
if newVersion.sysupgradeFileSize > MAX_VIABLE_IMAGE_SIZE then
print("Error: sysupgrade image file is too large, it will not run well (max. size: " .. MAX_VIABLE_IMAGE_SIZE .. " bytes)")
quit(4)
end
runAction("Copying release notes", "failed", 5, function()
return copyReleaseNotes(newVersion)
end)
runAction("Generating new index file", "could not generate index", 5, function()
return generateIndex(newVersion, isStable and stables or betas, isStable) return generateIndex(newVersion, isStable and stables or betas, isStable)
end) end)
runAction("Copying image files", "could not generate index", 4, function() runAction("Copying image files", "could not generate index", 5, function()
return copyImages(newVersion) return copyImages(newVersion)
end) end)
runAction("Copying release notes", "failed", 4, copyReleaseNotes)
io.stdout:write("* Building package feed directory...") io.stdout:write("* Building package feed directory...")
print("skipped - not implemented") print("skipped - not implemented")
-- runAction("Building package feed directory", "failed", 4, buildFeedDir) -- runAction("Building package feed directory", "failed", 5, buildFeedDir)
local answer = getYesNo("? Local updates directory will be synced to remote server, proceed? (y/n) ") local answer = getYesNo("? Local updates directory will be synced to remote server, proceed? (y/n) ")
@ -404,7 +455,7 @@ local function main()
quit(5) quit(5)
end end
runAction("About to sync files to server", "could not upload files", 5, uploadFiles) runAction("About to sync files to server", "could not upload files", 6, uploadFiles)
print("Done.") print("Done.")
quit() quit()

View File

@ -19,7 +19,7 @@
-- - _description_: A descriptive text usable by API clients -- - _description_: A descriptive text usable by API clients
-- - _min_, _max_, _regex_: optional constraints (min and max constrain value for numbers, or length for strings) -- - _min_, _max_, _regex_: optional constraints (min and max constrain value for numbers, or length for strings)
-- - _isValid_: an optional function which should return true for valid values and false for invalid ones -- - _isValid_: an optional function which should return true for valid values and false for invalid ones
-- - _subSection: optional: setting name of which current value is used as the uci section where this setting should be loaded from. Otherwise it's retrieved from the generic section. Setting subsection also means it will first try to get a default from subconf_defaults, if that doesn't exsist it will use the regular default -- - _subSection: optional: setting name of which current value is used as the uci section where this setting should be loaded from. Otherwise it's retrieved from the generic section. Setting subsection also means it will first try to get a default from subconf_defaults, if that doesn't exsist it will use the regular default
-- The configuration keys themselves document themselves rather well, hence they are not included in the generated documentation. -- The configuration keys themselves document themselves rather well, hence they are not included in the generated documentation.
-- --
-- NOTE: the all-caps definitions should be changed into configuration keys, or moved to a better location. -- NOTE: the all-caps definitions should be changed into configuration keys, or moved to a better location.
@ -106,7 +106,7 @@ M.printer_dimensions_x = {
default_delta_rostockmax = 0, default_delta_rostockmax = 0,
default_deltamaker = 0, default_deltamaker = 0,
default_kossel = 0, default_kossel = 0,
default_minifactory = 150, default_minifactory = 150,
subSection = 'printer_type', subSection = 'printer_type',
type = 'int', type = 'int',
description = '', description = '',
@ -335,7 +335,7 @@ M.doodle3d_tour_enabled = {
description = 'Show tour to new users' description = 'Show tour to new users'
} }
M.doodle3d_betas = { M.doodle3d_includeBetas = {
default = false, default = false,
type = 'bool', type = 'bool',
description = 'Update to beta releases', description = 'Update to beta releases',

View File

@ -49,7 +49,7 @@ end
function M.status(request, response) function M.status(request, response)
updater.setLogger(log) updater.setLogger(log)
updater.setUseCache(false) updater.setUseCache(false)
local includeBetas = settings.get('doodle3d.betas') local includeBetas = settings.get('doodle3d.includeBetas')
local success,status,msg = updater.getStatus(includeBetas) local success,status,msg = updater.getStatus(includeBetas)
response:addData('current_version', updater.formatVersion(status.currentVersion)) response:addData('current_version', updater.formatVersion(status.currentVersion))
@ -93,9 +93,9 @@ function M.download_POST(request, response)
updater.setState(updater.STATE.DOWNLOADING,"") updater.setState(updater.STATE.DOWNLOADING,"")
local vEnt, rv, msg local vEnt, rv, msg
local includeBetas = settings.get('doodle3d.includeBetas')
if not argVersion then if not argVersion then
local includeBetas = settings.get('doodle3d.betas')
local success,status,msg = updater.getStatus(includeBetas) local success,status,msg = updater.getStatus(includeBetas)
if not success then if not success then
updater.setState(updater.STATE.DOWNLOAD_FAILED, msg) updater.setState(updater.STATE.DOWNLOAD_FAILED, msg)
@ -126,7 +126,7 @@ function M.download_POST(request, response)
end end
end end
vEnt,msg = updater.findVersion(argVersion) vEnt,msg = updater.findVersion(argVersion, includeBetas)
if vEnt == nil then if vEnt == nil then
updater.setState(updater.STATE.DOWNLOAD_FAILED, "error searching version index (" .. msg .. ")") updater.setState(updater.STATE.DOWNLOAD_FAILED, "error searching version index (" .. msg .. ")")
response:setFail("error searching version index (" .. msg .. ")") response:setFail("error searching version index (" .. msg .. ")")
@ -163,9 +163,9 @@ function M.install_POST(request, response)
--local ssid = wifi.getSubstitutedSsid(settings.get('network.ap.ssid')) --local ssid = wifi.getSubstitutedSsid(settings.get('network.ap.ssid'))
--local rv,msg = netconf.enableAccessPoint(ssid) --local rv,msg = netconf.enableAccessPoint(ssid)
local includeBetas = settings.get('doodle3d.includeBetas')
if not argVersion then if not argVersion then
local includeBetas = settings.get('doodle3d.betas')
local success,status,msg = updater.getStatus(includeBetas) local success,status,msg = updater.getStatus(includeBetas)
if not success then if not success then
updater.setState(updater.STATE.INSTALL_FAILED, msg) updater.setState(updater.STATE.INSTALL_FAILED, msg)
@ -176,7 +176,7 @@ function M.install_POST(request, response)
end end
end end
vEnt,msg = updater.findVersion(argVersion) vEnt,msg = updater.findVersion(argVersion, includeBetas)
if vEnt == nil then if vEnt == nil then
updater.setState(updater.STATE.INSTALL_FAILED, "error searching version index (" .. msg .. ")") updater.setState(updater.STATE.INSTALL_FAILED, "error searching version index (" .. msg .. ")")
response:setFail("error searching version index (" .. msg .. ")") response:setFail("error searching version index (" .. msg .. ")")

View File

@ -67,7 +67,7 @@ M.WGET_OPTIONS = "-q -t 1 -T 30"
local verbosity = 0 -- set by parseCommandlineArguments() or @{setVerbosity} local verbosity = 0 -- set by parseCommandlineArguments() or @{setVerbosity}
local log = nil -- wifibox API can use M.setLogger to enable this module to use its logger local log = nil -- wifibox API can use M.setLogger to enable this module to use its logger
local useCache = true -- default, can be overwritten using @{setUseCache} local useCache = false -- default, can be overwritten using @{setUseCache}
local cachePath = M.DEFAULT_CACHE_PATH -- default, can be change using @{setCachePath} local cachePath = M.DEFAULT_CACHE_PATH -- default, can be change using @{setCachePath}
local baseUrl = M.DEFAULT_BASE_URL -- default, can be overwritten by M.setBaseUrl() local baseUrl = M.DEFAULT_BASE_URL -- default, can be overwritten by M.setBaseUrl()
@ -808,7 +808,7 @@ function M.flashImageVersion(versionEntry, noRetain, devType, isFactory)
return (rv == 0) and true or nil,rv return (rv == 0) and true or nil,rv
end end
--- Clears '*.bin' in the @{cachePath} directory. --- Clears '*.bin' and both index files in the @{cachePath} directory.
-- @treturn bool|nil True on success, or nil on error. -- @treturn bool|nil True on success, or nil on error.
-- @treturn ?string Descriptive message on error. -- @treturn ?string Descriptive message on error.
function M.clear() function M.clear()
@ -817,8 +817,16 @@ function M.clear()
D("Removing " .. cachePath .. "/doodle3d-wifibox-*.bin") D("Removing " .. cachePath .. "/doodle3d-wifibox-*.bin")
M.setState(M.STATE.NONE, "") M.setState(M.STATE.NONE, "")
local success = true
local rv = M.compatexecute('rm -f ' .. cachePath .. '/doodle3d-wifibox-*.bin') local rv = M.compatexecute('rm -f ' .. cachePath .. '/doodle3d-wifibox-*.bin')
return rv and true or nil,"could not remove image files" success = success and (rv == 0)
local rv = M.compatexecute('rm -f ' .. cachePath .. '/' .. M.IMAGE_STABLE_INDEX_FILE)
success = success and (rv == 0)
local rv = M.compatexecute('rm -f ' .. cachePath .. '/' .. M.IMAGE_BETA_INDEX_FILE)
success = success and (rv == 0)
--return success,"could not delete all files"
return true
end end
--- Set updater state. --- Set updater state.
@ -882,7 +890,7 @@ local function main()
P(1, "\t-q\t\tquiet mode") P(1, "\t-q\t\tquiet mode")
P(1, "\t-V\t\tverbose mode") P(1, "\t-V\t\tverbose mode")
P(1, "\t-c\t\tUse cache as much as possible") P(1, "\t-c\t\tUse cache as much as possible")
P(1, "\t-C\t\tDo not use the cache") P(1, "\t-C\t\tDo not use the cache (default)")
P(1, "\t-u <base_url>\tUse specified base URL (default: " .. M.DEFAULT_BASE_URL .. ")") P(1, "\t-u <base_url>\tUse specified base URL (default: " .. M.DEFAULT_BASE_URL .. ")")
P(1, "\t-b\t\tInclude beta releases") P(1, "\t-b\t\tInclude beta releases")
P(1, "\t-v\t\tShow current image version") P(1, "\t-v\t\tShow current image version")