pkulchenko / ZeroBranePackage

Packages for ZeroBrane Studio (https://studio.zerobrane.com)
MIT License
245 stars 149 forks source link

Package for Remote-Debugging via SSH #90

Open bassklampfe opened 3 years ago

bassklampfe commented 3 years ago

Dear @pkulchenko

It started as an attempt to use ZBS as remote IDE for Raspi, but it turned out to provide a generic package for remote debugging via SSH. Please kindly review the package and if you like it, add it to https://github.com/pkulchenko/ZeroBranePackage

But I also have some issues with this. During remote debug every output line is displayed twice in ZBS.

Twiddling with

debugger.port =9876
debugger.redirect = 'r'
debugger.hostname="192.168.1.27"

in project settings seems to have no effect on the rundebug argument passed to sshlua:frun(). Maybe you can give me advice.

So here it is sshlua.lua :

--[[
Remote debugging for ZeroBrane via ssh. Initial create to be used with Raspi,
but then shows up to be of general use.

#1. Setting up the remote system

How to read:
_#_         comment lines not to be entered
_remote>_   command to be entered in the remote system
_local>_    command to be entered on the local host
_ipaddr_    address of the remote system
_username_  username on the remote system

#1.1 Make ssh functional

 # install SSH server
 _remote>_ sudo apt install openssh-server
 # install SSH client
 _local>_ sudo apt install openssh-client
 # create as public/private rsa key pair
 _local>_ ssh-keygen -t rsa -b 4096 -N "" -f ~/.ssh/luassh
 # authenticate by key file
 _local>_ ssh-copy-id -i .ssh/luassh.pub username@ipaddr
 # test connection
 _local>_ ssh username@ipaddr

#1.2 Install required lua packages

 # install lua packages
 _remote>_ sudo apt install lua5.1 lua-socket
 # copy mobdebug to right place on remote system
 _local>_ scp /opt/zbstudio/lualibs/mobdebug/mobdebug.lua username@ipaddr:.
 _remote>_ sudo mkdir -p /usr/local/share/lua/5.1/
 _remote>_ sudo mv ~/mobdebug.lua /usr/local/share/lua/5.1/

#1.3 (Optional) packages I recommend (but this depends on your needs)

 _remote>_ sudo apt install lua-filesystem lua-bitop lua5.1-doc
 _remote>_ sudo cp /usr/share/doc/lua5.1-doc/etc/strict.lua /usr/local/share/lua/5.1/

#2. Setting up your project

* Install https://github.com/pkulchenko/ZeroBranePackage/blob/master/projectsettings.lua to Zerobrane
* Restart ZBS and open your project
* Invoke menu "Edit" / "Preferences" / "Settings: Project"
* Enter the following lines
---------------------
package "sshlua.lua"
ide.packages.sshlua.sshlogin="username@ipaddr"
ide.packages.sshlua.subdir="mytest"
---------------------
and save
* Rightclick on the "user.lua" tab and select "Open containing folder"
* Copy sshlua.lua into this folder
* Restart ZBS and open your project

#3. How to use

Just edit, run or debug files in your project, as normal. 
On Save files will be copied to subdir on remote system. 
Run and Debug will execute the script on remote system.
--]]

------------------------------------------------------------
--PACKAGE
------------------------------------------------------------
local sshlua=
{
    name = "sshlua",
    description = "ssh remote lua",
    author = "J.Jørgen von Bargen",
    version = 0.1,
    hasdebugger = true,
}

--
-- common utilities functions
--
local function quoteshell(path)
    return "'"..path:gsub("'","'\\''").."'"
end

local sprintf=string.format
local function printf(...)
    ide:Print((sprintf(...):gsub("%s+$","")))
end

local function vis(val)
    if type(val)~="string" then return tostring(val) end
    return '"'..val..'"'
end

function sshlua:get_remote_path(doc_path)
    --
    -- ignore files outside project
    --
    local proj_dir=ide:GetProject()
    if doc_path:sub(1,#proj_dir)~=proj_dir then return nil end
    local remote_path=doc_path:sub(#proj_dir+1)
    --
    -- ignore files in .zbstudio folder
    --
    if remote_path:match("^%.zbstudio") then return nil end
    --
    -- create relative to subdir
    --
    if self.subdir then remote_path=self.subdir.."/"..remote_path end
    return remote_path
end

function sshlua:frun(wfilename,rundebug)
    printf("interpreter:frun(%s,%s)\n",vis(wfilename:GetFullPath()),vis(rundebug))
    --
    -- verify the path belongs to the project and get remothe path
    --
    local local_path=wfilename:GetFullPath()
    local remote_path=self:get_remote_path(local_path)
    if not remote_path then return end
    --
    -- build command to be executed remotely
    --
    local luacmd=rundebug and sprintf("lua -e %q %q",rundebug,remote_path)
    or sprintf("lua %q",remote_path)
    printf("luacmd=[[%s]]\n",luacmd)
    local sshcmd="ssh -4 "..self.sshlogin.." "..quoteshell(luacmd).." 2>&1"
    printf("sshcmd=[[%s]]\n",sshcmd)
    local pid=CommandLineRun(sshcmd,ide:GetProject(),true,false)
    return pid
end

function sshlua:onRegister()
    ide:AddInterpreter(self.name, self)
    ProjectSetInterpreter(self.name)
end

function sshlua:onEditorSave(ed)
    --
    -- get document
    --
    local doc = ide:GetDocument(ed)
    if not doc then return end
    --
    -- verify the path belongs to the project and get remothe path
    --
    local local_path=doc:GetFilePath()
    local remote_path=self:get_remote_path(local_path)
    if not remote_path then return end

    --
    -- copy file to remote system via scp
    --
    local scpcmd=sprintf("scp -4 %s "..self.sshlogin..":%s",quoteshell(local_path),quoteshell(remote_path))
    local sts=os.execute(scpcmd.." 2>&1")
    printf("[[%s]] sts %s\n",scpcmd,vis(sts))

    if sts~=true then
        --
        -- maybe dir is missing ??? try to create
        --
        local remote_dir=remote_path:match("(.*)/") or "."
        local mkdircmd="ssh -4 "..self.sshlogin.." "..quoteshell("mkdir -vp \""..remote_dir.."\"")
        sts=os.execute(mkdircmd.." 2>&1")
        printf("[[%s]] sts %s\n",mkdircmd,vis(sts))
        if sts~=true then
            printf("cmd=[[%s]] failed\n",mkdircmd)
            return
        end
        --
        -- retry copy
        --
        sts=os.execute(scpcmd.." 2>&1")
        printf("[[%s]] sts %s\n",scpcmd,vis(sts))
    end
    if sts~=true then
        printf("cmd=[[%s]] failed\n",scpcmd)
        return
    end
end

return sshlua