From 009cebfee7f6884a0f84221c466ef99813b7bf0c Mon Sep 17 00:00:00 2001 From: Fedor Zhukov Date: Fri, 31 Oct 2025 07:25:30 +0100 Subject: [PATCH 1/3] Embed version in binary --- cmd/xtproxy/main.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/cmd/xtproxy/main.go b/cmd/xtproxy/main.go index ed88353..43294be 100644 --- a/cmd/xtproxy/main.go +++ b/cmd/xtproxy/main.go @@ -247,6 +247,16 @@ func mainServe() error { return fproxy.Wait() } +// versionCmd represents the version command +var Version = "dev" +var versionCmd = &cobra.Command{ + Use: "version", + Short: "print the version", + Run: func(cmd *cobra.Command, args []string) { + fmt.Println(Version) + }, +} + func main() { if err := rootCmd.Execute(); err != nil { if errors.Is(err, errUsage) { @@ -258,4 +268,5 @@ func main() { func init() { bindArgs() + rootCmd.AddCommand(versionCmd) } From 53f20fbe6caa0cea5cca6d044c3684e9710d864d Mon Sep 17 00:00:00 2001 From: Fedor Zhukov Date: Fri, 31 Oct 2025 06:56:53 +0100 Subject: [PATCH 2/3] Add test and release pipeline --- .github/workflows/release.yml | 106 ++++++++++++++++++++++++++++++++++ .github/workflows/test.yml | 40 +++++++++++++ Makefile | 14 ++++- 3 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..6a95b39 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,106 @@ +name: Release xtproxy + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +env: + IS_TEST_TAG: ${{ contains(github.ref_name, '-rc') || contains(github.ref_name, '-test') }} + +jobs: + build: + name: Build (${{ matrix.goos }}/${{ matrix.goarch }}) + runs-on: ubuntu-latest + strategy: + matrix: + include: + - goos: linux + goarch: amd64 + - goos: darwin + goarch: arm64 + + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: '1.24.x' + + - name: Derive version from tag + id: meta + shell: bash + run: | + ver="${GITHUB_REF_NAME#v}" + echo "version=${ver}" >> "$GITHUB_OUTPUT" + + - name: Build xtproxy + env: + CGO_ENABLED: "0" + GOOS: ${{ matrix.goos }} + GOARCH: ${{ matrix.goarch }} + VERSION: ${{ steps.meta.outputs.version }} + run: | + mkdir -p build + go build -trimpath \ + -ldflags="-s -w -X 'main.Version=${VERSION}'" \ + -o build/xtproxy ./cmd/xtproxy + + - name: Package as os/arch/xtproxy (tar.gz) + run: | + set -euo pipefail + os="${{ matrix.goos }}" + arch="${{ matrix.goarch }}" + mkdir -p "staging/${os}/${arch}" + cp build/xtproxy "staging/${os}/${arch}/xtproxy" + mkdir -p dist + tar -C staging -czf "dist/xtproxy_${os}_${arch}.tar.gz" "${os}/${arch}" + echo "Created dist/xtproxy_${os}_${arch}.tar.gz" + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: xtproxy-${{ matrix.goos }}-${{ matrix.goarch }} + path: dist/*.tar.gz + if-no-files-found: error + + release: + name: Create GitHub Release + runs-on: ubuntu-latest + needs: build + steps: + - name: Download all artifacts + uses: actions/download-artifact@v4 + with: + path: dist + + - name: Flatten artifact layout + shell: bash + run: | + mkdir -p out + find dist -type f -name '*.tar.gz' -exec mv -t out {} + + ls -l out + + - name: Checksums + working-directory: out + run: | + sha256sum *.tar.gz > checksums.txt + cat checksums.txt + + - name: Create / Update Release and upload assets + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.ref_name }} + name: Release ${{ github.ref_name }} + draft: ${{ env.IS_TEST_TAG == 'true' }} + prerelease: ${{ env.IS_TEST_TAG == 'true' }} + files: | + out/*.tar.gz + out/checksums.txt + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..4c4b87d --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,40 @@ +name: Test xtproxy + +on: [push] + +permissions: + contents: read + +jobs: + fmt: + name: go vet/fmt check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.24.x' + cache: true + - run: make lint + + test: + name: go test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.24.x' + cache: true + - run: make tests + + build: + name: go build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.24.x' + cache: true + - run: go build -v ./... diff --git a/Makefile b/Makefile index a725b74..5d3aa26 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all generate clean xtproxy tests +.PHONY: all generate clean xtproxy tests lint format all: xtproxy @@ -8,5 +8,13 @@ clean: xtproxy: go build ./cmd/xtproxy/ -tests: - go test ./pkg/... +test: + go test -v ./... + +lint: + @go vet ./... + @gofmt -s -d . + @test -z $$(gofmt -s -l .) + +format: + @gofmt -s -w . From 31ca194265ecae62bc2e270c50fc5922f3133eb2 Mon Sep 17 00:00:00 2001 From: Fedor Zhukov Date: Sun, 2 Nov 2025 09:31:44 +0100 Subject: [PATCH 3/3] Fix vet errors --- pkg/aferomount/mountfs.go | 2 +- pkg/aferomount/mountfs_test.go | 22 +++++++++++----------- pkg/xtproxy/http_webdav.go | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/pkg/aferomount/mountfs.go b/pkg/aferomount/mountfs.go index ad233da..073f6a9 100644 --- a/pkg/aferomount/mountfs.go +++ b/pkg/aferomount/mountfs.go @@ -44,7 +44,7 @@ func (m *MountFs) Name() string { func (m *MountFs) Mount(mountfs afero.Fs, path string) error { apath := absPath(path) - afs := afero.Afero{m.base} + afs := afero.Afero{Fs: m.base} dirExist, err := afs.DirExists(apath) if err != nil { return err diff --git a/pkg/aferomount/mountfs_test.go b/pkg/aferomount/mountfs_test.go index 76b58ea..13220d7 100644 --- a/pkg/aferomount/mountfs_test.go +++ b/pkg/aferomount/mountfs_test.go @@ -12,19 +12,19 @@ func TestMountfsRootMount(t *testing.T) { var afs afero.Afero memfs1 := afero.NewMemMapFs() - afs = afero.Afero{memfs1} + afs = afero.Afero{Fs: memfs1} assert.NoError(t, afs.MkdirAll("/a/b", 0755)) assert.NoError(t, afs.WriteFile("/a/file.txt", []byte("/a/file.txt: memfs1"), 0644)) memfs2 := afero.NewMemMapFs() - afs = afero.Afero{memfs2} + afs = afero.Afero{Fs: memfs2} assert.NoError(t, afs.MkdirAll("/a/b", 0755)) assert.NoError(t, afs.WriteFile("/a/file.txt", []byte("/a/file.txt: memfs2"), 0644)) var err error var expected string mountfs := NewMountFS(afero.NewReadOnlyFs(afero.NewMemMapFs())) - afs = afero.Afero{mountfs} + afs = afero.Afero{Fs: mountfs} _, err = afs.ReadFile("/a/file.txt") assert.Error(t, err, "/a/file.txt should not exist") @@ -56,15 +56,15 @@ func TestMountfsOverlappingMounts(t *testing.T) { var afs afero.Afero memfs1 := afero.NewMemMapFs() - afs = afero.Afero{memfs1} + afs = afero.Afero{Fs: memfs1} assert.NoError(t, afs.WriteFile("/a/file.txt", []byte("/a/file.txt: memfs1"), 0644)) memfs2 := afero.NewMemMapFs() - afs = afero.Afero{memfs2} + afs = afero.Afero{Fs: memfs2} assert.NoError(t, afs.WriteFile("/a/file.txt", []byte("/a/file.txt: memfs2"), 0644)) mountfs := NewMountFS(afero.NewMemMapFs()) - afs = afero.Afero{mountfs} + afs = afero.Afero{Fs: mountfs} var err error var got []byte @@ -81,10 +81,10 @@ func TestMountDirCreated(t *testing.T) { var afs afero.Afero memfs1 := afero.NewMemMapFs() - afs = afero.Afero{memfs1} + afs = afero.Afero{Fs: memfs1} assert.NoError(t, afs.WriteFile("/file.txt", []byte("/file.txt: memfs1"), 0644)) mountfs := NewMountFS(afero.NewMemMapFs()) - afs = afero.Afero{mountfs} + afs = afero.Afero{Fs: mountfs} exists, err := afs.DirExists("/a") assert.NoError(t, err) @@ -109,10 +109,10 @@ func TestMountDirExisted(t *testing.T) { var afs afero.Afero memfs1 := afero.NewMemMapFs() - afs = afero.Afero{memfs1} + afs = afero.Afero{Fs: memfs1} assert.NoError(t, afs.WriteFile("/file.txt", []byte("/file.txt: memfs1"), 0644)) mountfs := NewMountFS(afero.NewMemMapFs()) - afs = afero.Afero{mountfs} + afs = afero.Afero{Fs: mountfs} afs.MkdirAll("/a", 0777) exists, err := afs.DirExists("/a") @@ -139,7 +139,7 @@ func TestMountDirCreatedOverlap(t *testing.T) { memfs2 := afero.NewMemMapFs() mountfs := NewMountFS(afero.NewMemMapFs()) - afs := afero.Afero{mountfs} + afs := afero.Afero{Fs: mountfs} assert.NoError(t, mountfs.Mount(memfs1, "/")) assert.NoError(t, mountfs.Mount(memfs2, "/a")) diff --git a/pkg/xtproxy/http_webdav.go b/pkg/xtproxy/http_webdav.go index 132be25..0063c38 100644 --- a/pkg/xtproxy/http_webdav.go +++ b/pkg/xtproxy/http_webdav.go @@ -124,8 +124,8 @@ func (m httpURL) Fs() (afero.Fs, error) { if err != nil { return nil, err } - fs = &fsTrimPrefix{fs} - afs := &afero.FromIOFS{fs} + fs = &fsTrimPrefix{FS: fs} + afs := &afero.FromIOFS{FS: fs} return afs, nil }