Compare commits

...
Author SHA1 Message Date
Pierre Dubouilh eaf1342559 more conservative text editor 2024-09-28 18:02:16 +02:00
Pierre Dubouilh 31c8ee518a generalise docker images 2024-08-30 18:50:34 +02:00
Pierre Dubouilh cc703cd70d fixup docker CI 2024-08-30 18:50:34 +02:00
Pierre Dubouilh c963d1773b fixup case dockerfile
🤷
2024-08-30 14:49:10 +02:00
Pierre Dubouilh 623acc30a6 mpr 2024-08-30 14:44:06 +02:00
Pierre Dubouilh 746d6d55bc bump go to 1.23.0 & fixup build reproducibility 2024-08-30 14:44:06 +02:00
10 changed files with 89 additions and 114 deletions
+5 -10
View File
@@ -6,20 +6,15 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v1 uses: actions/setup-go@v5
with: with:
go-version: 1.19.1 go-version: 1.23.0
id: go id: go
- name: deps
run: npm i -g standard
- name: Check out code into the Go module directory
uses: actions/checkout@v1
with:
submodules: true
- name: Run - name: Run
run: make ci run: make ci
+11 -17
View File
@@ -10,33 +10,25 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v1 uses: actions/checkout@v4
with:
submodules: true
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.19.1
id: go
- name: Set env - name: Set env
run: echo "GIT_TAG=`echo $(git describe --tags --abbrev=0)`" >> $GITHUB_ENV run: echo "GIT_TAG=`echo $(git describe --tags --abbrev=0)`" >> $GITHUB_ENV
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v1.2.0 uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1.6.0
- name: Login to DockerHub - name: Login to DockerHub
uses: docker/login-action@v1.10.0 uses: docker/login-action@v3
with: with:
username: ${{ secrets.DOCKER_USER }} username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASSWORD }} password: ${{ secrets.DOCKER_PASSWORD }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and release on dockerhub - name: Build and release on dockerhub
uses: docker/build-push-action@v2.7.0 uses: docker/build-push-action@v6
with: with:
file: support/build.Dockerfile file: support/build.Dockerfile
push: true push: true
@@ -47,17 +39,19 @@ jobs:
run: make build-all run: make build-all
- name: "Release gh release versioned" - name: "Release gh release versioned"
uses: ncipollo/release-action@58ae73b360456532aafd58ee170c045abbeaee37 uses: ncipollo/release-action@v1
with: with:
allowUpdates: true allowUpdates: true
artifacts: "builds/*" artifacts: "builds/*"
bodyFile: "builds/buildout"
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- name: "Release gh release latest" - name: "Release gh release latest"
uses: ncipollo/release-action@58ae73b360456532aafd58ee170c045abbeaee37 uses: ncipollo/release-action@v1
with: with:
tag: latest tag: latest
name: Latest name: Latest
allowUpdates: true allowUpdates: true
artifacts: "builds/*" artifacts: "builds/*"
bodyFile: "builds/buildout"
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
-39
View File
@@ -1,39 +0,0 @@
name: rc
on:
push:
branches:
- 'rc/**'
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- name: Set up Go
uses: actions/setup-go@v1
with:
go-version: 1.19.1
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v1
with:
submodules: true
- name: Run
run: make ci
- name: Build all artifacts
run: make build-all
- name: "Release gh release prerelease"
uses: ncipollo/release-action@58ae73b360456532aafd58ee170c045abbeaee37
with:
allowUpdates: true
prerelease: true
tag: "rc"
artifacts: "builds/*"
token: ${{ secrets.GITHUB_TOKEN }}
+10 -12
View File
@@ -1,4 +1,4 @@
FLAGS := -ldflags "-s -w" -trimpath FLAGS := -trimpath
NOCGO := CGO_ENABLED=0 NOCGO := CGO_ENABLED=0
build:: build::
@@ -17,10 +17,7 @@ run-ro::
run-extra:: run-extra::
./gossa -verb=true -prefix="/fancy-path/" -k=false -symlinks=true test-fixture ./gossa -verb=true -prefix="/fancy-path/" -k=false -symlinks=true test-fixture
lint-js:: ci:: build-all test
standard
ci:: build-all test lint-js
echo "done" echo "done"
test:: test::
@@ -59,13 +56,14 @@ watch-test::
ls gossa.go gossa_test.go ui/* | entr -rc make test ls gossa.go gossa_test.go ui/* | entr -rc make test
build-all:: build build-all:: build
${NOCGO} GOOS=linux GOARCH=amd64 go build ${FLAGS} -o builds/gossa-linux-x64 go version
${NOCGO} GOOS=linux GOARCH=arm go build ${FLAGS} -o builds/gossa-linux-arm ${NOCGO} GOOS=linux GOARCH=amd64 go build ${FLAGS} -o builds/gossa-linux-x64
${NOCGO} GOOS=linux GOARCH=arm64 go build ${FLAGS} -o builds/gossa-linux-arm64 ${NOCGO} GOOS=linux GOARCH=arm go build ${FLAGS} -o builds/gossa-linux-arm
${NOCGO} GOOS=darwin GOARCH=amd64 go build ${FLAGS} -o builds/gossa-mac-x64 ${NOCGO} GOOS=linux GOARCH=arm64 go build ${FLAGS} -o builds/gossa-linux-arm64
${NOCGO} GOOS=darwin GOARCH=arm64 go build ${FLAGS} -o builds/gossa-mac-arm64 ${NOCGO} GOOS=darwin GOARCH=amd64 go build ${FLAGS} -o builds/gossa-mac-x64
${NOCGO} GOOS=windows GOARCH=amd64 go build ${FLAGS} -o builds/gossa-windows.exe ${NOCGO} GOOS=darwin GOARCH=arm64 go build ${FLAGS} -o builds/gossa-mac-arm64
sha256sum builds/* ${NOCGO} GOOS=windows GOARCH=amd64 go build ${FLAGS} -o builds/gossa-windows.exe
sha256sum builds/* | tee builds/buildout
clean:: clean::
rm -f gossa rm -f gossa
+1 -1
View File
@@ -1,3 +1,3 @@
module github.com/pldubouilh/gossa module github.com/pldubouilh/gossa
go 1.16 go 1.23.0
+32 -20
View File
@@ -45,11 +45,6 @@ var verb = flag.Bool("verb", false, "verbosity")
var skipHidden = flag.Bool("k", true, "\nskip hidden files") var skipHidden = flag.Bool("k", true, "\nskip hidden files")
var ro = flag.Bool("ro", false, "read only mode (no upload, rename, move, etc...)") var ro = flag.Bool("ro", false, "read only mode (no upload, rename, move, etc...)")
type rpcCall struct {
Call string `json:"call"`
Args []string `json:"args"`
}
var rootPath = "" var rootPath = ""
var handler http.Handler var handler http.Handler
@@ -134,7 +129,7 @@ func replyList(w http.ResponseWriter, r *http.Request, fullPath string, path str
if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") { if strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
w.Header().Set("Content-Type", "text/html") w.Header().Set("Content-Type", "text/html")
w.Header().Add("Content-Encoding", "gzip") w.Header().Add("Content-Encoding", "gzip")
gz, err := gzip.NewWriterLevel(w, gzip.BestSpeed) // BestSpeed is Much Faster than default - base on a very unscientific local test, and only ~30% larger (compression remains still very effective, ~6x) gz, err := gzip.NewWriterLevel(w, gzip.BestSpeed) // BestSpeed is Much Faster than default - base on a very unscientific local test
check(err) check(err)
defer gz.Close() defer gz.Close()
tmpl.Execute(gz, p) tmpl.Execute(gz, p)
@@ -151,7 +146,8 @@ func doContent(w http.ResponseWriter, r *http.Request) {
path := html.UnescapeString(r.URL.Path) path := html.UnescapeString(r.URL.Path)
defer exitPath(w, "get content", path) defer exitPath(w, "get content", path)
fullPath := enforcePath(path) fullPath, err := enforcePath(path)
check(err)
stat, errStat := os.Stat(fullPath) stat, errStat := os.Stat(fullPath)
check(errStat) check(errStat)
@@ -174,7 +170,9 @@ func upload(w http.ResponseWriter, r *http.Request) {
if err != nil && err != io.EOF { // errs EOF when no more parts to process if err != nil && err != io.EOF { // errs EOF when no more parts to process
check(err) check(err)
} }
dst, err := os.Create(enforcePath(path)) path, err = enforcePath(path)
check(err)
dst, err := os.Create(path)
check(err) check(err)
io.Copy(dst, part) io.Copy(dst, part)
w.Write([]byte("ok")) w.Write([]byte("ok"))
@@ -184,8 +182,9 @@ func zipRPC(w http.ResponseWriter, r *http.Request) {
zipPath := r.URL.Query().Get("zipPath") zipPath := r.URL.Query().Get("zipPath")
zipName := r.URL.Query().Get("zipName") zipName := r.URL.Query().Get("zipName")
defer exitPath(w, "zip", zipPath) defer exitPath(w, "zip", zipPath)
zipFullPath := enforcePath(zipPath) zipFullPath, err := enforcePath(zipPath)
_, err := os.Lstat(zipFullPath) check(err)
_, err = os.Lstat(zipFullPath)
check(err) check(err)
w.Header().Add("Content-Disposition", "attachment; filename=\""+zipName+".zip\"") w.Header().Add("Content-Disposition", "attachment; filename=\""+zipName+".zip\"")
zipWriter := zip.NewWriter(w) zipWriter := zip.NewWriter(w)
@@ -203,7 +202,7 @@ func zipRPC(w http.ResponseWriter, r *http.Request) {
return nil // hidden files not allowed return nil // hidden files not allowed
} }
if f.Mode()&os.ModeSymlink != 0 { if f.Mode()&os.ModeSymlink != 0 {
panic(errors.New("symlink not allowed in zip downloads")) // filepath.Walk doesnt support symlinks check(errors.New("symlink not allowed in zip downloads")) // filepath.Walk doesnt support symlinks
} }
header, err := zip.FileInfoHeader(f) header, err := zip.FileInfoHeader(f)
@@ -224,39 +223,52 @@ func zipRPC(w http.ResponseWriter, r *http.Request) {
} }
func rpc(w http.ResponseWriter, r *http.Request) { func rpc(w http.ResponseWriter, r *http.Request) {
var err error type rpcCall struct {
Call string `json:"call"`
Args []string `json:"args"`
}
var rpc rpcCall var rpc rpcCall
defer exitPath(w, "rpc", rpc) defer exitPath(w, "rpc", rpc)
bodyBytes, err := io.ReadAll(r.Body) bodyBytes, err := io.ReadAll(r.Body)
check(err) check(err)
json.Unmarshal(bodyBytes, &rpc) json.Unmarshal(bodyBytes, &rpc)
path0, err := enforcePath(rpc.Args[0])
path1 := ""
check(err)
if len(rpc.Args) > 1 {
path1, err = enforcePath(rpc.Args[1])
check(err)
}
if rpc.Call == "mkdirp" { if rpc.Call == "mkdirp" {
err = os.MkdirAll(enforcePath(rpc.Args[0]), os.ModePerm) err = os.MkdirAll(path0, os.ModePerm)
} else if rpc.Call == "mv" { } else if rpc.Call == "mv" && len(rpc.Args) == 2 {
err = os.Rename(enforcePath(rpc.Args[0]), enforcePath(rpc.Args[1])) err = os.Rename(path0, path1)
} else if rpc.Call == "rm" { } else if rpc.Call == "rm" {
err = os.RemoveAll(enforcePath(rpc.Args[0])) err = os.RemoveAll(path0)
} else {
err = errors.New("invalid rpc call")
} }
check(err) check(err)
w.Write([]byte("ok")) w.Write([]byte("ok"))
} }
func enforcePath(p string) string { func enforcePath(p string) (string, error) {
joined := filepath.Join(rootPath, strings.TrimPrefix(p, *extraPath)) joined := filepath.Join(rootPath, strings.TrimPrefix(p, *extraPath))
fp, err := filepath.Abs(joined) fp, err := filepath.Abs(joined)
sl, _ := filepath.EvalSymlinks(fp) // err skipped as it would error for unexistent files (RPC check). The actual behaviour is tested below sl, _ := filepath.EvalSymlinks(fp) // err skipped as it would error for inexistent files (RPC check). The actual behaviour is tested below
// panic if we had a error getting absolute path, // panic if we had a error getting absolute path,
// ... or if path doesnt contain the prefix path we expect, // ... or if path doesnt contain the prefix path we expect,
// ... or if we're skipping hidden folders, and one is requested, // ... or if we're skipping hidden folders, and one is requested,
// ... or if we're skipping symlinks, path exists, and a symlink out of bound requested // ... or if we're skipping symlinks, path exists, and a symlink out of bound requested
if err != nil || !strings.HasPrefix(fp, rootPath) || *skipHidden && strings.Contains(p, "/.") || !*symlinks && len(sl) > 0 && !strings.HasPrefix(sl, rootPath) { if err != nil || !strings.HasPrefix(fp, rootPath) || *skipHidden && strings.Contains(p, "/.") || !*symlinks && len(sl) > 0 && !strings.HasPrefix(sl, rootPath) {
panic(errors.New("invalid path")) return "", errors.New("invalid path")
} }
return fp return fp, nil
} }
func main() { func main() {
+3 -1
View File
@@ -29,7 +29,9 @@ a simple UI comes as default, featuring :
[nix](https://search.nixos.org/packages?channel=unstable&show=gossa&from=0&size=50&sort=relevance&type=packages&query=gossa) - e.g. `nix-shell -p gossa` [nix](https://search.nixos.org/packages?channel=unstable&show=gossa&from=0&size=50&sort=relevance&type=packages&query=gossa) - e.g. `nix-shell -p gossa`
binaries are available on the [release page](https://github.com/pldubouilh/gossa/releases) - or simply `make build` this repo. [mpr](https://mpr.makedeb.org/packages/gossa)
binaries are available on the [release page](https://github.com/pldubouilh/gossa/releases)
all builds are reproducible, checkout the hashes on the release page. all builds are reproducible, checkout the hashes on the release page.
+2 -1
View File
@@ -1,4 +1,5 @@
FROM golang:1.18 as builder FROM docker.io/library/golang:1.23.0-alpine AS builder
RUN apk add --no-cache make
WORKDIR /gossaSrc WORKDIR /gossaSrc
COPY . /gossaSrc COPY . /gossaSrc
RUN make RUN make
+1
View File
@@ -1 +1,2 @@
B!!! B!!!
test
+23 -12
View File
@@ -153,8 +153,11 @@ function rpc (call, args, cb) {
xhr.open('POST', location.origin + window.extraPath + '/rpc') xhr.open('POST', location.origin + window.extraPath + '/rpc')
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8') xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8')
xhr.send(JSON.stringify({ call, args })) xhr.send(JSON.stringify({ call, args }))
xhr.onload = cb xhr.onload = () => cb(false)
xhr.onerror = () => flicker(sadBadge) xhr.onerror = () => {
flicker(sadBadge)
cb(true)
}
} }
const mkdirCall = (path, cb) => rpc('mkdirp', [prependPath(path)], cb) const mkdirCall = (path, cb) => rpc('mkdirp', [prependPath(path)], cb)
@@ -315,28 +318,36 @@ const textTypes = ['.txt', '.rtf', '.md', '.markdown', '.log', '.yaml', '.yml']
const isTextFile = src => src && textTypes.find(type => src.toLocaleLowerCase().includes(type)) const isTextFile = src => src && textTypes.find(type => src.toLocaleLowerCase().includes(type))
let fileEdited let fileEdited
function saveText (quitting) { function saveText (cb) {
const formData = new FormData() const formData = new FormData()
formData.append(fileEdited, editor.value) formData.append(fileEdited, editor.value)
const path = encodeURIComponent(decodeURI(location.pathname) + fileEdited) const fname = fileEdited + ".swp"
const path = encodeURIComponent(decodeURI(location.pathname) + fname)
upload(0, formData, path, () => { upload(0, formData, path, () => {
toast.style.display = 'none' toast.style.display = 'none'
if (!quitting) return cb()
clearInterval(window.padTimer)
window.onbeforeunload = null
resetView()
softPrev()
refresh()
}, () => { }, () => {
toast.style.display = 'block' toast.style.display = 'block'
if (!quitting) return
alert('cant save!\r\nleave window open to resume saving\r\nwhen connection back up') alert('cant save!\r\nleave window open to resume saving\r\nwhen connection back up')
}) })
} }
function padOff () { function padOff () {
if (!isEditorMode()) { return } if (!isEditorMode()) { return }
saveText(true) const swapfile = fileEdited + ".swp"
saveText(() => {
mvCall(prependPath(swapfile), prependPath(fileEdited), err => {
if (err) {
alert('cant save!\r\nleave window open to resume saving\r\nwhen connection back up')
return
}
clearInterval(window.padTimer)
window.onbeforeunload = null
resetView()
softPrev()
refresh()
})
})
return true return true
} }