2014-02-04 13:41:13 +01:00
|
|
|
#!/usr/bin/env lua
|
2014-02-04 19:38:41 +01:00
|
|
|
--#!/usr/bin/env lua -l strict
|
2014-02-04 13:41:13 +01:00
|
|
|
|
2014-02-20 10:02:42 +01:00
|
|
|
--TODO: replace prints with D() function from update manager and other slightly smarter mechanisms?
|
|
|
|
|
2014-02-04 13:41:13 +01:00
|
|
|
local function ERR(msg) print(msg) end
|
|
|
|
|
2014-02-04 19:38:41 +01:00
|
|
|
local ok, pl = pcall(require, 'pl.import_into')
|
2014-02-04 13:41:13 +01:00
|
|
|
if not ok then
|
2014-02-04 19:38:41 +01:00
|
|
|
ERR('This script requires the Penlight library')
|
2014-02-19 16:57:22 +01:00
|
|
|
os.exit(2)
|
2014-02-04 13:41:13 +01:00
|
|
|
end
|
2014-02-04 19:38:41 +01:00
|
|
|
pl = pl()
|
|
|
|
|
|
|
|
local lfs = require('lfs') -- assume this exists since it's required by penlight as well
|
2014-02-04 13:41:13 +01:00
|
|
|
|
2014-02-19 11:50:25 +01:00
|
|
|
local argStash = arg
|
|
|
|
arg = nil
|
|
|
|
local upmgr = require('d3d-update-mgr') -- arg must be nil for the update manager to load as module
|
|
|
|
arg = argStash
|
|
|
|
|
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
|
|
|
|
-----------------------------
|
|
|
|
-- CONSTANTS AND VARIABLES --
|
|
|
|
-----------------------------
|
|
|
|
|
2014-02-04 13:41:13 +01:00
|
|
|
local D3D_REPO_FIRMWARE_NAME = 'doodle3d-firmware'
|
|
|
|
local D3D_REPO_CLIENT_NAME = 'doodle3d-client'
|
|
|
|
local D3D_REPO_PRINT3D_NAME = 'print3d'
|
2014-02-19 12:35:34 +01:00
|
|
|
local IMAGE_BASENAME = 'doodle3d-wifibox'
|
2014-02-20 10:02:42 +01:00
|
|
|
local INDEX_BACKUP_SUFFIX = 'bkp'
|
2014-02-04 13:41:13 +01:00
|
|
|
|
2014-02-19 12:35:34 +01:00
|
|
|
local deviceType = 'tl-mr3020' -- or 'tl-wr703'
|
2014-02-19 16:57:22 +01:00
|
|
|
local lock = nil
|
2014-02-04 13:41:13 +01:00
|
|
|
local paths = {}
|
|
|
|
|
|
|
|
|
2014-02-19 12:35:34 +01:00
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
-----------------------
|
|
|
|
-- UTILITY FUNCTIONS --
|
|
|
|
-----------------------
|
|
|
|
|
|
|
|
local function quit(ev)
|
|
|
|
if lock then lock:free() end
|
|
|
|
os.exit(ev or 0)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function md5sum(file)
|
|
|
|
local rv,_,sum = pl.utils.executeex('md5 -q "' .. file .. '"')
|
|
|
|
|
|
|
|
return rv and sum:sub(1, -2) or nil
|
|
|
|
end
|
|
|
|
|
2014-02-04 13:41:13 +01:00
|
|
|
local function detectOpenWrtRoot()
|
|
|
|
local f = io.open('Makefile', 'r')
|
|
|
|
local line = f and f:read('*line')
|
|
|
|
local rv = (line and line:find('# Makefile for OpenWrt') == 1) and true or false
|
|
|
|
|
|
|
|
if f then f:close() end
|
|
|
|
return rv
|
|
|
|
end
|
|
|
|
|
|
|
|
-- returns uri (file path) of the wifibox feed, nil if not found or nil+msg on error
|
|
|
|
-- recognized feed names are 'wifibox' and 'doodle3d' (case-insensitive)
|
|
|
|
local function getWifiboxFeedRoot(feedsFile)
|
|
|
|
local typ, nam, uri = nil, nil, nil
|
|
|
|
local lineNo = 1
|
|
|
|
local f = io.open(feedsFile, 'r')
|
|
|
|
|
|
|
|
if not f then return nil, "could not open '" .. feedsFile .. '"' end
|
|
|
|
|
|
|
|
for line in f:lines() do
|
|
|
|
typ, nam, uri = line:match('^([^%s]+)%s+([^%s]+)%s+([^%s]+)$')
|
|
|
|
|
|
|
|
if not (typ and nam and uri) then
|
|
|
|
f:close()
|
|
|
|
return uri or nil, "could not parse line " .. feedsFile .. "#" .. lineNo
|
|
|
|
end
|
|
|
|
|
|
|
|
local commented = (typ:find('#') == 1)
|
|
|
|
if not commented and (nam:lower() == 'wifibox' or nam:lower() == 'doodle3d') then
|
|
|
|
break
|
|
|
|
else
|
|
|
|
typ, nam, uri = nil, nil, nil
|
|
|
|
end
|
|
|
|
|
|
|
|
lineNo = lineNo + 1
|
|
|
|
end
|
|
|
|
|
|
|
|
if uri and not (typ == 'src-link' or typ == 'src-cpy') then return nil, "d3d feed has wrong type '" .. typ .. "', use 'src-link' or 'src-cpy'" end
|
|
|
|
|
|
|
|
f:close()
|
|
|
|
return uri
|
|
|
|
end
|
|
|
|
|
|
|
|
-- TODO: pass table to functions to fill in? if they all return either true or nil+msg, that could be used for display of ok/msg
|
|
|
|
-- returns true on success, false on error, and displays meaningful messages
|
|
|
|
--local function runCheck(msg, processFunc)
|
|
|
|
-- io.stdout:write(msg .. "... ")
|
|
|
|
-- return processFunc(--[[ hmm ]]--)
|
|
|
|
--end
|
|
|
|
|
|
|
|
|
2014-02-19 12:35:34 +01:00
|
|
|
local function constructImageName(version, devType, sysupOrFactory)
|
|
|
|
return IMAGE_BASENAME .. '-' .. upmgr.formatVersion(version) .. '-' .. devType .. '-' .. sysupOrFactory .. '.bin'
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2014-02-04 13:41:13 +01:00
|
|
|
|
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
--------------------
|
|
|
|
-- MAIN FUNCTIONS --
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
local function prepare()
|
|
|
|
local msg = nil
|
2014-02-04 13:41:13 +01:00
|
|
|
|
|
|
|
io.stdout:write("Checking if working directory is the OpenWrt root... ")
|
|
|
|
local isOpenWrtRoot = detectOpenWrtRoot()
|
|
|
|
if isOpenWrtRoot then
|
2014-02-19 12:35:34 +01:00
|
|
|
paths.wrt = pl.path.currentdir()
|
|
|
|
print("found (" .. paths.wrt .. ")")
|
2014-02-04 13:41:13 +01:00
|
|
|
else
|
|
|
|
print("unrecognized directory, try changing directories or using -wrt-root")
|
2014-02-19 16:57:22 +01:00
|
|
|
return nil
|
2014-02-04 13:41:13 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
io.stdout:write("Looking for Doodle3D feed path... ")
|
|
|
|
local d3dFeed,msg = getWifiboxFeedRoot('feeds.conf')
|
|
|
|
if d3dFeed then
|
|
|
|
print("found " .. d3dFeed)
|
|
|
|
else
|
2014-02-04 19:38:41 +01:00
|
|
|
if msg then print("not found: " .. msg) else print("not found.") end
|
2014-02-19 16:57:22 +01:00
|
|
|
return nil
|
2014-02-04 13:41:13 +01:00
|
|
|
end
|
|
|
|
|
2014-02-20 10:02:42 +01:00
|
|
|
paths.firmware = pl.path.join(d3dFeed, D3D_REPO_FIRMWARE_NAME)
|
|
|
|
paths.client = pl.path.join(d3dFeed, D3D_REPO_CLIENT_NAME)
|
|
|
|
paths.print3d = pl.path.join(d3dFeed, D3D_REPO_PRINT3D_NAME)
|
2014-02-04 13:41:13 +01:00
|
|
|
|
2014-02-04 19:38:41 +01:00
|
|
|
-- if empty, try to choose something sensible
|
2014-02-04 13:41:13 +01:00
|
|
|
if not paths.cache or paths.cache == '' then
|
2014-02-20 10:02:42 +01:00
|
|
|
paths.cache = '/tmp/d3d-release-dir'
|
2014-02-04 19:38:41 +01:00
|
|
|
end
|
|
|
|
io.stdout:write("Attempting to use " .. paths.cache .. " as cache dir... ")
|
|
|
|
local rv,msg = pl.dir.makepath(paths.cache)
|
|
|
|
if not rv then
|
|
|
|
print("could not create path (" .. msg .. ").")
|
2014-02-19 16:57:22 +01:00
|
|
|
return nil
|
2014-02-04 19:38:41 +01:00
|
|
|
end
|
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
lock,msg = lfs.lock_dir(paths.cache)
|
2014-02-04 19:38:41 +01:00
|
|
|
if not lock then
|
|
|
|
print("could not obtain directory lock (" .. msg .. ").")
|
2014-02-19 16:57:22 +01:00
|
|
|
return nil
|
2014-02-04 19:38:41 +01:00
|
|
|
else
|
2014-02-19 12:35:34 +01:00
|
|
|
print("ok")
|
2014-02-04 13:41:13 +01:00
|
|
|
end
|
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2014-02-20 10:02:42 +01:00
|
|
|
local function collectLocalInfo()
|
|
|
|
local info = {}
|
|
|
|
|
|
|
|
-- temporary fields required for copying image files
|
|
|
|
info.factoryImgPath = pl.path.join(paths.wrt, 'bin/ar71xx/openwrt-ar71xx-generic-' .. deviceType .. '-v1-squashfs-factory.bin')
|
|
|
|
info.sysupgradeImgPath = pl.path.join(paths.wrt, 'bin/ar71xx/openwrt-ar71xx-generic-' .. deviceType .. '-v1-squashfs-sysupgrade.bin')
|
|
|
|
|
|
|
|
info.version = upmgr.parseVersion(pl.file.read(pl.path.join(paths.firmware, 'src/FIRMWARE-VERSION')))
|
|
|
|
if not info.version then return nil,"could not determine current firmware version" end
|
|
|
|
|
|
|
|
info.factoryFileSize = pl.path.getsize(info.factoryImgPath)
|
|
|
|
if not info.factoryFileSize then return nil,"could not determine size for factory image" end
|
|
|
|
|
|
|
|
info.sysupgradeFileSize = pl.path.getsize(info.sysupgradeImgPath)
|
|
|
|
if not info.sysupgradeFileSize then return nil,"could not determine size for sysupgrade image" end
|
|
|
|
|
|
|
|
info.factoryMD5 = md5sum(info.factoryImgPath)
|
|
|
|
info.sysupgradeMD5 = md5sum(info.sysupgradeImgPath)
|
|
|
|
if not info.factoryMD5 or not info.sysupgradeMD5 then return nil,"could not determine MD5 sum for image(s)" end
|
|
|
|
|
|
|
|
info.factoryFilename = constructImageName(info.version, deviceType, 'factory')
|
|
|
|
info.sysupgradeFilename = constructImageName(info.version, deviceType, 'sysupgrade')
|
|
|
|
info.timestamp = os.time()
|
|
|
|
|
|
|
|
return info
|
|
|
|
end
|
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
local function fetchVersionInfo()
|
|
|
|
local msg,stables,betas = nil,nil,nil
|
|
|
|
|
|
|
|
stables,msg = upmgr.getAvailableVersions('stables')
|
|
|
|
if not stables then return nil,msg end
|
2014-02-04 13:41:13 +01:00
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
betas,msg = upmgr.getAvailableVersions('betas')
|
|
|
|
if not betas then return nil,msg end
|
|
|
|
|
|
|
|
return stables, betas
|
|
|
|
end
|
2014-02-19 12:35:34 +01:00
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
local function generateIndex(newVersion, versionTable, isStable)
|
2014-02-20 10:02:42 +01:00
|
|
|
local indexFilename = isStable and upmgr.IMAGE_STABLE_INDEX_FILE or upmgr.IMAGE_BETA_INDEX_FILE
|
|
|
|
local sortedVers = pl.List(versionTable)
|
|
|
|
sortedVers:sort(function(a, b)
|
|
|
|
return upmgr.compareVersions(a.version, b.version, a.timestamp, b.timestamp) < 0
|
|
|
|
end)
|
|
|
|
|
|
|
|
local indexPath = pl.path.join(paths.cache, indexFilename)
|
|
|
|
pl.file.copy(indexPath, pl.path.join(paths.cache, indexFilename..'.'..INDEX_BACKUP_SUFFIX))
|
|
|
|
local idxFile = io.open(pl.path.join(paths.cache, indexFilename), 'w')
|
|
|
|
if not idxFile then return nil,"could not open index file for writing" end
|
|
|
|
|
|
|
|
sortedVers:foreach(function(el)
|
|
|
|
idxFile:write("Version: " .. upmgr.formatVersion(el.version) .. "\n")
|
|
|
|
idxFile:write("Files: " .. el.sysupgradeFilename .. "; " .. el.factoryFilename .. "\n")
|
|
|
|
idxFile:write("FileSize: " .. el.sysupgradeFileSize .. "; " .. el.factoryFileSize .. "\n")
|
|
|
|
idxFile:write("MD5: " .. el.sysupgradeMD5 .. "; " .. el.factoryMD5 .. "\n")
|
|
|
|
idxFile:write("ReleaseDate: " .. upmgr.formatDate(el.timestamp) .. "\n")
|
|
|
|
end)
|
|
|
|
|
|
|
|
idxFile:close()
|
2014-02-19 16:57:22 +01:00
|
|
|
return 0
|
|
|
|
end
|
|
|
|
|
2014-02-20 10:02:42 +01:00
|
|
|
local function ensureFilePresent(src, tgt)
|
|
|
|
-- print("About to copy '" .. src .. "' => '" .. tgt .. "'")
|
|
|
|
local srcMd5, tgtMd5 = md5sum(src), md5sum(tgt)
|
|
|
|
|
|
|
|
if not srcMd5 then return nil,"source file does not exist" end
|
|
|
|
if tgtMd5 and srcMd5 ~= tgtMd5 then return nil,"target file already exists but is different from source file" end
|
|
|
|
|
|
|
|
if not tgtMd5 then
|
|
|
|
if not pl.file.copy(src, tgt, false) then return nil,"could not copy file" end
|
|
|
|
end
|
|
|
|
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
local function copyImages(newVersion)
|
2014-02-20 10:02:42 +01:00
|
|
|
local rv,msg
|
|
|
|
rv,msg = ensureFilePresent(newVersion.factoryImgPath, pl.path.join(paths.cache, newVersion.factoryFilename))
|
|
|
|
if not rv then return nil,msg end
|
|
|
|
|
|
|
|
rv,msg = ensureFilePresent(newVersion.sysupgradeImgPath, pl.path.join(paths.cache, newVersion.sysupgradeFilename))
|
|
|
|
if not rv then return nil,msg end
|
|
|
|
|
|
|
|
return true
|
2014-02-19 16:57:22 +01:00
|
|
|
end
|
2014-02-19 12:35:34 +01:00
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
local function main()
|
|
|
|
print("Doodle3D release script")
|
|
|
|
-- local opts = parseOptions(arg)
|
|
|
|
--
|
|
|
|
-- if opts['wrt-root'] then changedir(opts['wrt-root']) end
|
|
|
|
-- if opts['cache-dir'] then paths.cache = opts['cache-dir'] end
|
|
|
|
|
|
|
|
if not prepare() then quit(1) end
|
|
|
|
|
|
|
|
-- initialize update manager script
|
2014-02-19 11:50:25 +01:00
|
|
|
upmgr.setUseCache(false)
|
|
|
|
upmgr.setVerbosity(1)
|
|
|
|
upmgr.setCachePath(paths.cache)
|
2014-02-04 19:38:41 +01:00
|
|
|
|
2014-02-20 10:02:42 +01:00
|
|
|
local newVersion,msg = collectLocalInfo()
|
|
|
|
if not newVersion then
|
|
|
|
print("Error: could not collect local version information (" .. msg .. ")")
|
|
|
|
quit(3)
|
|
|
|
end
|
2014-02-19 12:35:34 +01:00
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
local stables,betas = fetchVersionInfo()
|
|
|
|
if not stables then
|
|
|
|
print("Error: could not get version information (" .. betas .. ")")
|
|
|
|
quit(1)
|
2014-02-19 12:35:34 +01:00
|
|
|
end
|
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
local isStable = (newVersion.version.suffix == nil)
|
|
|
|
print("Rolling release for firmware version " .. upmgr.formatVersion(newVersion.version) .. " (type: " .. (isStable and "stable" or "beta") .. ").")
|
|
|
|
|
|
|
|
if upmgr.findVersion(newVersion.version, stables) or upmgr.findVersion(newVersion.version, betas) then
|
|
|
|
print("Error: firmware version " .. upmgr.formatVersion(newVersion.version) .. " already exists")
|
|
|
|
quit(3)
|
|
|
|
end
|
|
|
|
|
2014-02-20 10:02:42 +01:00
|
|
|
io.stdout:write("Generating new index file...")
|
2014-02-19 16:57:22 +01:00
|
|
|
if not generateIndex(newVersion, isStable and stables or betas, isStable) then
|
|
|
|
print("Error: could not generate index")
|
|
|
|
quit(4)
|
2014-02-20 10:02:42 +01:00
|
|
|
else
|
|
|
|
print("ok")
|
2014-02-19 16:57:22 +01:00
|
|
|
end
|
|
|
|
|
2014-02-20 10:02:42 +01:00
|
|
|
io.stdout:write("Copying image files...")
|
|
|
|
local rv,msg = copyImages(newVersion)
|
|
|
|
if not rv then
|
|
|
|
print("Error: could not copy images (" .. msg .. ")")
|
2014-02-19 16:57:22 +01:00
|
|
|
quit(4)
|
2014-02-20 10:02:42 +01:00
|
|
|
else
|
|
|
|
print("ok")
|
2014-02-19 12:35:34 +01:00
|
|
|
end
|
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
|
2014-02-20 10:02:42 +01:00
|
|
|
-- pl.pretty.dump(newVersion)
|
|
|
|
-- print("stables: "); pl.pretty.dump(stables)
|
|
|
|
-- print("===========================");
|
|
|
|
-- print("betas: "); pl.pretty.dump(betas)
|
2014-02-19 16:57:22 +01:00
|
|
|
|
|
|
|
|
2014-02-19 12:35:34 +01:00
|
|
|
--if requested, fetch images and packages (i.e., mirror whole directory)
|
|
|
|
|
|
|
|
--run sanity checks
|
|
|
|
|
|
|
|
--check whether newVersion is not conflicting with or older than anything in corresponding table
|
|
|
|
--add newVersion to correct table and generate updated index file
|
2014-02-04 13:41:13 +01:00
|
|
|
|
2014-02-19 16:57:22 +01:00
|
|
|
quit()
|
2014-02-04 19:38:41 +01:00
|
|
|
end
|
2014-02-04 13:41:13 +01:00
|
|
|
|
|
|
|
|
2014-02-04 19:38:41 +01:00
|
|
|
main()
|