~shulhan/awwan

62bf073d3d5b55a7e76cf5d9cebefdc9c00cbcee — Shulhan 8 months ago 4ec4ca7
all: replace module "share" with "pakakeh.go"

The "share" project has been moved to SourceHut with new name
"pakakeh.go".
M awwan.go => awwan.go +7 -7
@@ 13,8 13,8 @@ import (
	"strings"
	"time"

	"github.com/shuLhan/share/lib/ini"
	"github.com/shuLhan/share/lib/ssh/config"
	"git.sr.ht/~shulhan/pakakeh.go/lib/ini"
	"git.sr.ht/~shulhan/pakakeh.go/lib/sshconfig"

	"git.sr.ht/~shulhan/awwan/internal"
)


@@ 69,7 69,7 @@ type Awwan struct {
	cryptoc *cryptoContext

	// All the Host values from SSH config files.
	sshConfig *config.Config
	sshConfig *sshconfig.Config

	httpd *httpServer



@@ 406,7 406,7 @@ func (aww *Awwan) Play(ctx context.Context, req *ExecRequest) (err error) {
		sessionDir = filepath.Dir(req.scriptPath)

		ses        *Session
		sshSection *config.Section
		sshSection *sshconfig.Section
		pos        linePosition
	)



@@ 504,7 504,7 @@ func (aww *Awwan) loadSSHConfig() (err error) {
	var (
		logp = `loadSSHConfig`

		baseDirConfig *config.Config
		baseDirConfig *sshconfig.Config
		homeDir       string
		configFile    string
	)


@@ 515,14 515,14 @@ func (aww *Awwan) loadSSHConfig() (err error) {
	}

	configFile = filepath.Join(homeDir, defSSHDir, defSSHConfig)
	aww.sshConfig, err = config.Load(configFile)
	aww.sshConfig, err = sshconfig.Load(configFile)
	if err != nil {
		return fmt.Errorf("%s: %w", logp, err)
	}

	configFile = filepath.Join(aww.BaseDir, defSSHDir, defSSHConfig)

	baseDirConfig, err = config.Load(configFile)
	baseDirConfig, err = sshconfig.Load(configFile)
	if err != nil {
		return fmt.Errorf("%s: %w", logp, err)
	}

M awwan_decrypt_test.go => awwan_decrypt_test.go +2 -2
@@ 10,8 10,8 @@ import (
	"path/filepath"
	"testing"

	"github.com/shuLhan/share/lib/test"
	"github.com/shuLhan/share/lib/test/mock"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test/mock"
)

func TestAwwanDecrypt(t *testing.T) {

M awwan_encrypt_test.go => awwan_encrypt_test.go +2 -2
@@ 10,8 10,8 @@ import (
	"path/filepath"
	"testing"

	"github.com/shuLhan/share/lib/test"
	"github.com/shuLhan/share/lib/test/mock"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test/mock"
)

func TestAwwanEncrypt(t *testing.T) {

M awwan_env_test.go => awwan_env_test.go +1 -1
@@ 8,7 8,7 @@ import (
	"path/filepath"
	"testing"

	"github.com/shuLhan/share/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func TestAwwanEnvGet(t *testing.T) {

M awwan_local_test.go => awwan_local_test.go +2 -2
@@ 14,8 14,8 @@ import (
	"testing"
	"time"

	"github.com/shuLhan/share/lib/test"
	"github.com/shuLhan/share/lib/test/mock"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test/mock"
)

func TestAwwanLocal(t *testing.T) {

M awwan_play_test.go => awwan_play_test.go +1 -1
@@ 13,7 13,7 @@ import (
	"path/filepath"
	"testing"

	"github.com/shuLhan/share/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

type testCaseGetPut struct {

M awwan_sudo_test.go => awwan_sudo_test.go +2 -2
@@ 13,8 13,8 @@ import (
	"path/filepath"
	"testing"

	"github.com/shuLhan/share/lib/test"
	"github.com/shuLhan/share/lib/test/mock"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test/mock"
)

func TestAwwan_Local_SudoGet(t *testing.T) {

M crypto_context.go => crypto_context.go +1 -1
@@ 18,7 18,7 @@ import (
	"os"
	"path/filepath"

	libcrypto "github.com/shuLhan/share/lib/crypto"
	libcrypto "git.sr.ht/~shulhan/pakakeh.go/lib/crypto"
)

// defFilePrivateKey define the default private key file name.

M doc.go => doc.go +1 -1
@@ 49,7 49,7 @@ local or remote.

# References

[1] https://pkg.go.dev/github.com/shuLhan/share/lib/ini
[1] https://pkg.go.dev/git.sr.ht/~shulhan/pakakeh.go/lib/ini

[2] https://git-scm.com/docs/git-config#_configuration_file
*/

M exec_request.go => exec_request.go +1 -1
@@ 9,7 9,7 @@ import (
	"os"
	"path/filepath"

	"github.com/shuLhan/share/lib/mlog"
	"git.sr.ht/~shulhan/pakakeh.go/lib/mlog"
)

// defLogTimeFormat define the default log time format.

M exec_response.go => exec_response.go +1 -1
@@ 9,7 9,7 @@ import (
	"sync"
	"time"

	"github.com/shuLhan/share/lib/http/sseclient"
	"git.sr.ht/~shulhan/pakakeh.go/lib/http/sseclient"
)

// ExecResponse contains the request and output of command execution, from

M go.mod => go.mod +10 -10
@@ 3,28 3,28 @@

module git.sr.ht/~shulhan/awwan

go 1.20
go 1.21

require (
	git.sr.ht/~shulhan/ciigo v0.11.0
	git.sr.ht/~shulhan/ciigo v0.11.1-0.20240321082653-aed750e52cec
	git.sr.ht/~shulhan/pakakeh.go v0.53.2-0.20240321104707-cee16b8ead85
	github.com/evanw/esbuild v0.19.8
	github.com/shuLhan/share v0.53.0
)

require (
	git.sr.ht/~shulhan/asciidoctor-go v0.5.1 // indirect
	github.com/yuin/goldmark v1.6.0 // indirect
	git.sr.ht/~shulhan/asciidoctor-go v0.5.2-0.20240305110034-dc67158aeeb6 // indirect
	github.com/yuin/goldmark v1.7.0 // indirect
	github.com/yuin/goldmark-meta v1.1.0 // indirect
	golang.org/x/crypto v0.18.0 // indirect
	golang.org/x/net v0.20.0 // indirect
	golang.org/x/sys v0.16.0 // indirect
	golang.org/x/term v0.16.0 // indirect
	golang.org/x/crypto v0.21.0 // indirect
	golang.org/x/net v0.22.0 // indirect
	golang.org/x/sys v0.18.0 // indirect
	golang.org/x/term v0.18.0 // indirect
	gopkg.in/yaml.v2 v2.4.0 // indirect
)

replace github.com/evanw/esbuild => github.com/shuLhan/esbuild v0.19.9-0.20231209212032-2dc984ffc5f1

replace golang.org/x/crypto => git.sr.ht/~shulhan/go-x-crypto v0.17.1-0.20231222080754-445dd75cd339
replace golang.org/x/crypto => git.sr.ht/~shulhan/go-x-crypto v0.21.1-0.20240316083930-db093b454c7e

//replace github.com/shuLhan/share => ../share


M go.sum => go.sum +16 -16
@@ 1,24 1,24 @@
git.sr.ht/~shulhan/asciidoctor-go v0.5.1 h1:TuuLo+N61+qsXkiFgtiW5W1q7xHzeSID4zH+ci5J8ic=
git.sr.ht/~shulhan/asciidoctor-go v0.5.1/go.mod h1:5audSCN6jDr2+/cMvx1MdZxkCurjl/k6A5OGYWRtB0o=
git.sr.ht/~shulhan/ciigo v0.11.0 h1:t8/PqVQVOsG025WLjNjJSI4S37jN5CkY+LyC+zd1snI=
git.sr.ht/~shulhan/ciigo v0.11.0/go.mod h1:pyt2kxKvipCAO+jrjHuEXOWJ2h0ss/hnO9j7Xot3JHc=
git.sr.ht/~shulhan/go-x-crypto v0.17.1-0.20231222080754-445dd75cd339 h1:iq2/NVwTZvvs4QWBJaBt1r+ZNaXf/cAOAO5k8Eplqtg=
git.sr.ht/~shulhan/go-x-crypto v0.17.1-0.20231222080754-445dd75cd339/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
git.sr.ht/~shulhan/asciidoctor-go v0.5.2-0.20240305110034-dc67158aeeb6 h1:/0I9F83ZgfDjVDRTSNGxYaZroOxGg3o5m9GlqnWmYXs=
git.sr.ht/~shulhan/asciidoctor-go v0.5.2-0.20240305110034-dc67158aeeb6/go.mod h1:ht3glgf7w0J2HxyKoy/ZMg//okyw3F6GrwH4/KlwhxA=
git.sr.ht/~shulhan/ciigo v0.11.1-0.20240321082653-aed750e52cec h1:BydPiP72IvvnnV5OD5kwwTOv2jIgkgC6pQ9NhyZqeM8=
git.sr.ht/~shulhan/ciigo v0.11.1-0.20240321082653-aed750e52cec/go.mod h1:8V5f4C/y34ndK9MGpYM2rq70f22kraeX0xLt96ZULuY=
git.sr.ht/~shulhan/go-x-crypto v0.21.1-0.20240316083930-db093b454c7e h1:E8rBuql7phkpfhEn7QTxNQDj1Eg3OzcTFXbmoYGK34U=
git.sr.ht/~shulhan/go-x-crypto v0.21.1-0.20240316083930-db093b454c7e/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
git.sr.ht/~shulhan/pakakeh.go v0.53.2-0.20240321104707-cee16b8ead85 h1:XAIvSk+vMue69jBlpfXdyG6yK6QD4bhEapIS6juMtXQ=
git.sr.ht/~shulhan/pakakeh.go v0.53.2-0.20240321104707-cee16b8ead85/go.mod h1:ys7WNtXm03x0M59oqrqBjXnc+wRCX8JBXyE/W8+KVbw=
github.com/shuLhan/esbuild v0.19.9-0.20231209212032-2dc984ffc5f1 h1:U4DRlREmugTNkevukauQjjUsz82o3YRjtbxDILoN/Xs=
github.com/shuLhan/esbuild v0.19.9-0.20231209212032-2dc984ffc5f1/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
github.com/shuLhan/share v0.53.0 h1:hxQQbUWKav0pGaVarakEBX7hqJsyfd5jGDG3l6mcAsU=
github.com/shuLhan/share v0.53.0/go.mod h1:97/BcWdLau8i+xeFvPHdyqph1HgxVBSVhQEUIyCmgRc=
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=

M http_server.go => http_server.go +17 -17
@@ 18,9 18,9 @@ import (
	"strconv"
	"strings"

	libhttp "github.com/shuLhan/share/lib/http"
	"github.com/shuLhan/share/lib/http/sseclient"
	"github.com/shuLhan/share/lib/memfs"
	libhttp "git.sr.ht/~shulhan/pakakeh.go/lib/http"
	"git.sr.ht/~shulhan/pakakeh.go/lib/http/sseclient"
	"git.sr.ht/~shulhan/pakakeh.go/lib/memfs"

	"git.sr.ht/~shulhan/awwan/internal"
)


@@ 91,7 91,7 @@ func newHTTPServer(aww *Awwan, address string) (httpd *httpServer, err error) {
		return nil, fmt.Errorf(`%s: %w`, logp, err)
	}

	var serverOpts = &libhttp.ServerOptions{
	var serverOpts = libhttp.ServerOptions{
		Memfs:   internal.MemfsWui,
		Address: address,
	}


@@ 113,7 113,7 @@ func newHTTPServer(aww *Awwan, address string) (httpd *httpServer, err error) {
func (httpd *httpServer) registerEndpoints() (err error) {
	var logp = `registerEndpoints`

	err = httpd.RegisterEndpoint(&libhttp.Endpoint{
	err = httpd.RegisterEndpoint(libhttp.Endpoint{
		Method:       libhttp.RequestMethodGet,
		Path:         pathAwwanAPIFs,
		RequestType:  libhttp.RequestTypeQuery,


@@ 124,7 124,7 @@ func (httpd *httpServer) registerEndpoints() (err error) {
		return fmt.Errorf("%s: %w", logp, err)
	}

	err = httpd.RegisterEndpoint(&libhttp.Endpoint{
	err = httpd.RegisterEndpoint(libhttp.Endpoint{
		Method:       libhttp.RequestMethodDelete,
		Path:         pathAwwanAPIFs,
		RequestType:  libhttp.RequestTypeJSON,


@@ 135,7 135,7 @@ func (httpd *httpServer) registerEndpoints() (err error) {
		return fmt.Errorf("%s: %w", logp, err)
	}

	err = httpd.RegisterEndpoint(&libhttp.Endpoint{
	err = httpd.RegisterEndpoint(libhttp.Endpoint{
		Method:       libhttp.RequestMethodPost,
		Path:         pathAwwanAPIFs,
		RequestType:  libhttp.RequestTypeJSON,


@@ 146,7 146,7 @@ func (httpd *httpServer) registerEndpoints() (err error) {
		return fmt.Errorf("%s: %w", logp, err)
	}

	err = httpd.RegisterEndpoint(&libhttp.Endpoint{
	err = httpd.RegisterEndpoint(libhttp.Endpoint{
		Method:       libhttp.RequestMethodPut,
		Path:         pathAwwanAPIFs,
		RequestType:  libhttp.RequestTypeJSON,


@@ 164,7 164,7 @@ func (httpd *httpServer) registerEndpoints() (err error) {
		ResponseType: libhttp.ResponseTypeJSON,
		Call:         httpd.Decrypt,
	}
	err = httpd.RegisterEndpoint(&epDecrypt)
	err = httpd.RegisterEndpoint(epDecrypt)
	if err != nil {
		return fmt.Errorf(`%s %q: %w`, logp, epDecrypt.Path, err)
	}


@@ 176,12 176,12 @@ func (httpd *httpServer) registerEndpoints() (err error) {
		ResponseType: libhttp.ResponseTypeJSON,
		Call:         httpd.Encrypt,
	}
	err = httpd.RegisterEndpoint(&epEncrypt)
	err = httpd.RegisterEndpoint(epEncrypt)
	if err != nil {
		return fmt.Errorf(`%s %q: %w`, logp, epEncrypt.Path, err)
	}

	err = httpd.RegisterEndpoint(&libhttp.Endpoint{
	err = httpd.RegisterEndpoint(libhttp.Endpoint{
		Method:       libhttp.RequestMethodPost,
		Path:         pathAwwanAPIExecute,
		RequestType:  libhttp.RequestTypeJSON,


@@ 194,7 194,7 @@ func (httpd *httpServer) registerEndpoints() (err error) {

	// Register endpoint to cancel execution.

	err = httpd.RegisterEndpoint(&libhttp.Endpoint{
	err = httpd.RegisterEndpoint(libhttp.Endpoint{
		Method:       libhttp.RequestMethodDelete,
		Path:         pathAwwanAPIExecute,
		RequestType:  libhttp.RequestTypeJSON,


@@ 208,7 208,7 @@ func (httpd *httpServer) registerEndpoints() (err error) {
	// Register Server-sent events to tail the execution state and
	// output.

	var epExecuteTail = &libhttp.SSEEndpoint{
	var epExecuteTail = libhttp.SSEEndpoint{
		Path: pathAwwanAPIExecuteTail,
		Call: httpd.ExecuteTail,
	}


@@ 407,7 407,7 @@ func (httpd *httpServer) FSGet(epr *libhttp.EndpointRequest) (resb []byte, err e
		path string
	)

	path = epr.HttpRequest.Form.Get(paramNamePath)
	path = epr.HTTPRequest.Form.Get(paramNamePath)
	if len(path) == 0 {
		res.Code = http.StatusOK
		res.Data = httpd.memfsBase


@@ 759,7 759,7 @@ func (httpd *httpServer) Execute(epr *libhttp.EndpointRequest) (resb []byte, err
func (httpd *httpServer) ExecuteCancel(epr *libhttp.EndpointRequest) (resb []byte, err error) {
	var (
		endRes      = &libhttp.EndpointResponse{}
		execID      = epr.HttpRequest.Form.Get(paramNameID)
		execID      = epr.HTTPRequest.Form.Get(paramNameID)
		ctxDoCancel = httpd.idContextCancel[execID]
	)



@@ 808,7 808,7 @@ func (httpd *httpServer) ExecuteCancel(epr *libhttp.EndpointRequest) (resb []byt
//	data: invalid or empty ID ${id}
func (httpd *httpServer) ExecuteTail(sseconn *libhttp.SSEConn) {
	var (
		execID  = sseconn.HttpRequest.Form.Get(paramNameID)
		execID  = sseconn.HTTPRequest.Form.Get(paramNameID)
		execRes = httpd.idExecRes[execID]
	)
	if execRes == nil {


@@ 817,7 817,7 @@ func (httpd *httpServer) ExecuteTail(sseconn *libhttp.SSEConn) {
	}

	var (
		lastEventIDStr = sseconn.HttpRequest.Header.Get(libhttp.HeaderLastEventID)
		lastEventIDStr = sseconn.HTTPRequest.Header.Get(libhttp.HeaderLastEventID)
		lastEventID    int64
	)


M http_server_play_test.go => http_server_play_test.go +2 -2
@@ 16,8 16,8 @@ import (
	"strings"
	"testing"

	libhttp "github.com/shuLhan/share/lib/http"
	"github.com/shuLhan/share/lib/test"
	libhttp "git.sr.ht/~shulhan/pakakeh.go/lib/http"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

// Here is what happened,

M http_server_test.go => http_server_test.go +38 -26
@@ 17,10 17,10 @@ import (
	"testing"
	"time"

	libhttp "github.com/shuLhan/share/lib/http"
	"github.com/shuLhan/share/lib/http/sseclient"
	libnet "github.com/shuLhan/share/lib/net"
	"github.com/shuLhan/share/lib/test"
	libhttp "git.sr.ht/~shulhan/pakakeh.go/lib/http"
	"git.sr.ht/~shulhan/pakakeh.go/lib/http/sseclient"
	libnet "git.sr.ht/~shulhan/pakakeh.go/lib/net"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func TestHttpServer_Decrypt(t *testing.T) {


@@ 238,7 238,7 @@ func TestHttpServer_Execute(t *testing.T) {

	var (
		clientOpts = libhttp.ClientOptions{
			ServerUrl: fmt.Sprintf(`http://%s`, address),
			ServerURL: fmt.Sprintf(`http://%s`, address),
		}
		reqJSON = tdata.Input[`local:/local.aww:1-`]



@@ 246,7 246,7 @@ func TestHttpServer_Execute(t *testing.T) {
		cl          *libhttp.Client
	)

	cl = libhttp.NewClient(&clientOpts)
	cl = libhttp.NewClient(clientOpts)

	err = json.Unmarshal(reqJSON, &execRequest)
	if err != nil {


@@ 254,16 254,21 @@ func TestHttpServer_Execute(t *testing.T) {
	}

	var (
		resBody []byte
		buf     bytes.Buffer
		clientReq = libhttp.ClientRequest{
			Path:   pathAwwanAPIExecute,
			Params: &execRequest,
		}
		clientResp *libhttp.ClientResponse
	)

	_, resBody, err = cl.PostJSON(pathAwwanAPIExecute, nil, &execRequest)
	clientResp, err = cl.PostJSON(clientReq)
	if err != nil {
		t.Fatal(err)
	}

	json.Indent(&buf, resBody, ``, `  `)
	var buf bytes.Buffer

	json.Indent(&buf, clientResp.Body, ``, `  `)

	var expResp = string(tdata.Output[`local:/local.aww:1-`])



@@ 276,7 281,7 @@ func TestHttpServer_Execute(t *testing.T) {

	res.Data = &execResp

	err = json.Unmarshal(resBody, &res)
	err = json.Unmarshal(clientResp.Body, &res)
	if err != nil {
		t.Fatal(err)
	}


@@ 364,7 369,7 @@ func TestHttpServer_ExecuteCancel(t *testing.T) {

	var (
		clientOpts = libhttp.ClientOptions{
			ServerUrl: fmt.Sprintf(`http://%s`, address),
			ServerURL: fmt.Sprintf(`http://%s`, address),
		}
		reqJSON = tdata.Input[`local:/cancel.aww:1-`]



@@ 372,7 377,7 @@ func TestHttpServer_ExecuteCancel(t *testing.T) {
		cl          *libhttp.Client
	)

	cl = libhttp.NewClient(&clientOpts)
	cl = libhttp.NewClient(clientOpts)

	err = json.Unmarshal(reqJSON, &execRequest)
	if err != nil {


@@ 380,16 385,21 @@ func TestHttpServer_ExecuteCancel(t *testing.T) {
	}

	var (
		resBody []byte
		buf     bytes.Buffer
		clientReq = libhttp.ClientRequest{
			Path:   pathAwwanAPIExecute,
			Params: &execRequest,
		}
		clientResp *libhttp.ClientResponse
	)

	_, resBody, err = cl.PostJSON(pathAwwanAPIExecute, nil, &execRequest)
	clientResp, err = cl.PostJSON(clientReq)
	if err != nil {
		t.Fatal(err)
	}

	json.Indent(&buf, resBody, ``, `  `)
	var buf bytes.Buffer

	json.Indent(&buf, clientResp.Body, ``, `  `)

	var expResp = string(tdata.Output[`local:/cancel.aww:1-`])



@@ 402,7 412,7 @@ func TestHttpServer_ExecuteCancel(t *testing.T) {

	res.Data = &execResp

	err = json.Unmarshal(resBody, &res)
	err = json.Unmarshal(clientResp.Body, &res)
	if err != nil {
		t.Fatal(err)
	}


@@ 457,20 467,22 @@ func TestHttpServer_ExecuteCancel(t *testing.T) {

func testDoExecuteCancel(t *testing.T, tdata *test.Data, cl *libhttp.Client, execID string) {
	var (
		params = url.Values{}

		resBody []byte
		err     error
		clientReq = libhttp.ClientRequest{
			Path: pathAwwanAPIExecute,
			Params: url.Values{
				paramNameID: []string{execID},
			},
		}
		clientResp *libhttp.ClientResponse
		err        error
	)

	params.Set(paramNameID, execID)

	_, resBody, err = cl.Delete(pathAwwanAPIExecute, nil, params)
	clientResp, err = cl.Delete(clientReq)
	if err != nil {
		t.Fatal(err)
	}

	test.Assert(t, `Cancel response`, string(tdata.Output[`local:/cancel.aww:1-:response`]), string(resBody))
	test.Assert(t, `Cancel response`, string(tdata.Output[`local:/cancel.aww:1-:response`]), string(clientResp.Body))
}

func TestHttpServer_FSGet(t *testing.T) {

M internal/cmd/www-awwan/main.go => internal/cmd/www-awwan/main.go +3 -2
@@ 14,9 14,10 @@ import (
	"path/filepath"
	"syscall"

	"git.sr.ht/~shulhan/awwan/internal"
	"git.sr.ht/~shulhan/ciigo"
	"github.com/shuLhan/share/lib/memfs"
	"git.sr.ht/~shulhan/pakakeh.go/lib/memfs"

	"git.sr.ht/~shulhan/awwan/internal"
)

const defAddress = `127.0.0.1:4358`

M internal/cmd/www-awwan/memfs.go => internal/cmd/www-awwan/memfs.go +4 -4
@@ 1,9 1,9 @@
// Code generated by github.com/shuLhan/share/lib/memfs DO NOT EDIT.
// Code generated by git.sr.ht/~shulhan/pakakeh.go/lib/memfs DO NOT EDIT.

package main

import (
	"github.com/shuLhan/share/lib/memfs"
	"git.sr.ht/~shulhan/pakakeh.go/lib/memfs"
)

func generate__wui_doc() *memfs.Node {


@@ 211,7 211,7 @@ func init() {
		Opts: &memfs.Options{
			Root:        "_wui/doc",
			MaxFileSize: 5242880,
			Includes: []string{
			Includes:    []string{
			},
			Excludes: []string{
				`.*\.adoc$`,


@@ 219,7 219,7 @@ func init() {
				`^\..*`,
			},
			Embed: memfs.EmbedOptions{
				CommentHeader: ``,
				CommentHeader:  ``,
				PackageName:    "main",
				VarName:        "MemfsWww",
				GoFileName:     "internal/cmd/www-awwan/memfs.go",

M internal/internal.go => internal/internal.go +7 -3
@@ 11,10 11,10 @@ import (
	"time"

	"git.sr.ht/~shulhan/ciigo"
	"git.sr.ht/~shulhan/pakakeh.go/lib/memfs"
	"git.sr.ht/~shulhan/pakakeh.go/lib/mlog"
	"github.com/evanw/esbuild/pkg/api"
	esapi "github.com/evanw/esbuild/pkg/api"
	"github.com/shuLhan/share/lib/memfs"
	"github.com/shuLhan/share/lib/mlog"
)

// MemfsWui contains the embedded files under _wui for web-user interface.


@@ 189,7 189,11 @@ func Watch() {
					mlog.Errf(`%s %q: %s`, logp, ns.Node.Path, err)
					continue
				}
				node.Update(&ns.Node, 0)
				err = node.Update(&ns.Node, 0)
				if err != nil {
					mlog.Errf(`%s %q: %s`, logp, ns.Node.Path, err)
					continue
				}
				embedCount++

			case strings.HasSuffix(ns.Node.SysPath, DocConvertOpts.HTMLTemplate):

M internal/memfs_wui.go => internal/memfs_wui.go +4 -4
@@ 1,11 1,11 @@
// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info>
// SPDX-License-Identifier: GPL-3.0-or-later
// Code generated by github.com/shuLhan/share/lib/memfs DO NOT EDIT.
// Code generated by git.sr.ht/~shulhan/pakakeh.go/lib/memfs DO NOT EDIT.

package internal

import (
	"github.com/shuLhan/share/lib/memfs"
	"git.sr.ht/~shulhan/pakakeh.go/lib/memfs"
)

func generate__wui() *memfs.Node {


@@ 245,7 245,7 @@ func init() {
		Opts: &memfs.Options{
			Root:        "_wui",
			MaxFileSize: 5242880,
			Includes: []string{
			Includes:    []string{
				`.*\.(css|js|html|jpg|png|ico)$`,
			},
			Excludes: []string{


@@ 255,7 255,7 @@ func init() {
				`_wui/wui`,
			},
			Embed: memfs.EmbedOptions{
				CommentHeader: `// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info>
				CommentHeader:  `// SPDX-FileCopyrightText: 2021 M. Shulhan <ms@kilabit.info>
// SPDX-License-Identifier: GPL-3.0-or-later
`,
				PackageName:    "internal",

M line_range_test.go => line_range_test.go +1 -1
@@ 8,7 8,7 @@ package awwan
import (
	"testing"

	"github.com/shuLhan/share/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func TestParseLineRange(t *testing.T) {

M script_test.go => script_test.go +1 -1
@@ 9,7 9,7 @@ import (
	"bytes"
	"testing"

	"github.com/shuLhan/share/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func TestJoinRequireStatements(t *testing.T) {

M session.go => session.go +5 -5
@@ 17,10 17,10 @@ import (
	"strings"
	"text/template"

	"github.com/shuLhan/share/lib/ini"
	libos "github.com/shuLhan/share/lib/os"
	libexec "github.com/shuLhan/share/lib/os/exec"
	"github.com/shuLhan/share/lib/ssh/config"
	"git.sr.ht/~shulhan/pakakeh.go/lib/ini"
	libos "git.sr.ht/~shulhan/pakakeh.go/lib/os"
	libexec "git.sr.ht/~shulhan/pakakeh.go/lib/os/exec"
	"git.sr.ht/~shulhan/pakakeh.go/lib/sshconfig"
)

// Session manage environment and SSH client.


@@ 623,7 623,7 @@ func (ses *Session) generatePaths() (err error) {
	return nil
}

func (ses *Session) initSSHClient(req *ExecRequest, sshSection *config.Section) (err error) {
func (ses *Session) initSSHClient(req *ExecRequest, sshSection *sshconfig.Section) (err error) {
	var (
		logp          = "initSSHClient"
		lastIdentFile string

M session_test.go => session_test.go +1 -1
@@ 11,7 11,7 @@ import (
	"path/filepath"
	"testing"

	"github.com/shuLhan/share/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func TestSession_generatePaths(t *testing.T) {

M ssh_client.go => ssh_client.go +5 -5
@@ 12,9 12,9 @@ import (
	"path/filepath"
	"strings"

	"github.com/shuLhan/share/lib/ssh"
	"github.com/shuLhan/share/lib/ssh/config"
	"github.com/shuLhan/share/lib/ssh/sftp"
	"git.sr.ht/~shulhan/pakakeh.go/lib/ssh"
	"git.sr.ht/~shulhan/pakakeh.go/lib/ssh/sftp"
	"git.sr.ht/~shulhan/pakakeh.go/lib/sshconfig"
)

// sshClient contains clients for SSH and SFTP (SSH File Transport Protocol


@@ 24,7 24,7 @@ import (
// If the remote server does not support SFTP, it will fallback to use scp
// command using SSH connection.
type sshClient struct {
	section *config.Section
	section *sshconfig.Section

	conn  *ssh.Client
	sftpc *sftp.Client


@@ 40,7 40,7 @@ type sshClient struct {
//
// Once connection established, the client create new temporary directory on
// server at dirTmp for sudoGet or sudoPut operations.
func newSSHClient(req *ExecRequest, section *config.Section) (sshc *sshClient, err error) {
func newSSHClient(req *ExecRequest, section *sshconfig.Section) (sshc *sshClient, err error) {
	var logp = `newSSHClient`

	req.mlog.Outf(`--- SSH connection: %s@%s:%s`,

M statement.go => statement.go +1 -1
@@ 11,7 11,7 @@ import (
	"strconv"
	"strings"

	libexec "github.com/shuLhan/share/lib/os/exec"
	libexec "git.sr.ht/~shulhan/pakakeh.go/lib/os/exec"
)

const (

M statement_test.go => statement_test.go +1 -1
@@ 8,7 8,7 @@ package awwan
import (
	"testing"

	"github.com/shuLhan/share/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func TestParseStatement(t *testing.T) {

M sudo_test.go => sudo_test.go +1 -1
@@ 10,7 10,7 @@ import (
	"context"
	"testing"

	"github.com/shuLhan/share/lib/test"
	"git.sr.ht/~shulhan/pakakeh.go/lib/test"
)

func TestExecLocal_sudo(t *testing.T) {