From e7aeffe707d637d20c0ac8e42394f48152eb367c Mon Sep 17 00:00:00 2001 From: Adam Snyder Date: Sun, 23 Jun 2024 15:38:28 -0700 Subject: [PATCH] chore: add release automation --- .github/.goreleaser.yaml | 37 ++++++++++ .github/release-please-config.json | 6 ++ .github/workflows/ci.yaml | 73 ++++++++++++++++++++ .github/workflows/test.yaml | 38 ----------- .gitignore | 2 + README.md | 10 ++- main.go | 104 +++++++++++++++++++++++++++-- 7 files changed, 227 insertions(+), 43 deletions(-) create mode 100644 .github/.goreleaser.yaml create mode 100644 .github/release-please-config.json create mode 100644 .github/workflows/ci.yaml delete mode 100644 .github/workflows/test.yaml diff --git a/.github/.goreleaser.yaml b/.github/.goreleaser.yaml new file mode 100644 index 0000000..1f2642f --- /dev/null +++ b/.github/.goreleaser.yaml @@ -0,0 +1,37 @@ +# yaml-language-server: $schema=https://goreleaser.com/static/schema.json + +version: 2 + +builds: + - goos: + - linux + - windows + - darwin + goarch: + - amd64 + - arm64 + ldflags: + - "-X main.version={{ .Version }}" + +universal_binaries: + - replace: false + +archives: + - format: tar.gz + name_template: >- + {{ .ProjectName }}_ + {{- title .Os }}_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "all" }}universal + {{- else }}{{ .Arch }}{{ end }} + {{- if .Arm }}v{{ .Arm }}{{ end }} + format_overrides: + - goos: windows + format: zip + wrap_in_directory: true + +checksum: + name_template: checksums.txt + +changelog: + disable: true diff --git a/.github/release-please-config.json b/.github/release-please-config.json new file mode 100644 index 0000000..ae7a1c6 --- /dev/null +++ b/.github/release-please-config.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "release-type": "go", + "packages": {}, + "bump-minor-pre-major": true +} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..9cba8ca --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,73 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow + +on: + push: + branches: + - main + pull_request: {} + +jobs: + check-generated: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.22" + - run: go mod tidy + - run: go generate ./... + - run: git diff --exit-code + + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.22" + - run: go install + - run: go test -cover ./... + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.22" + - uses: golangci/golangci-lint-action@v4 + with: + version: v1.59.1 + + release-please: + runs-on: ubuntu-latest + needs: [check-generated, test, lint] + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + outputs: + release_created: ${{ steps.release-please.outputs.release_created }} + tag_name: ${{ steps.release-please.outputs.tag_name }} + version: ${{ steps.release-please.outputs.version }} + steps: + - uses: googleapis/release-please-action@v4 + with: + token: ${{ secrets.PAT }} + config-file: .github/release-please-config.json + + release: + runs-on: ubuntu-latest + needs: [release-please] + if: needs.release-please.outputs.release_created + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - run: git fetch --tags --force + - uses: actions/setup-go@v5 + with: + go-version: "1.22" + - uses: goreleaser/goreleaser-action@v6 + with: + version: "~> v2" + args: release --clean --config .github/.goreleaser.yaml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml deleted file mode 100644 index 1f29d34..0000000 --- a/.github/workflows/test.yaml +++ /dev/null @@ -1,38 +0,0 @@ -# yaml-language-server: $schema=https://json.schemastore.org/github-workflow - -on: - push: - branches: - - main - pull_request: {} - -jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.22" - - run: | - go test -cover ./... - - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.22" - - uses: golangci/golangci-lint-action@v4 - with: - version: v1.59.1 - - install: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.22" - - run: go install . diff --git a/.gitignore b/.gitignore index 069b93a..691095a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ *.out /openapiv3-lsp +/dist +/tmp diff --git a/README.md b/README.md index 75a6dbe..b41e4d2 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,6 @@ for me to implement in this language server. ## Features - ### Language Features - [x] Jump to definition @@ -36,10 +35,18 @@ for me to implement in this language server. ## Installation +### Using Go + ```bash go install github.com/armsnyder/openapiv3-lsp@latest ``` +### From GitHub Releases + +Download the latest release from [GitHub releases](https://github.com/armsnyder/openapiv3-lsp/releases). + +## Usage + ### Neovim Configuration Example Assuming you are using Neovim and have the installed openapiv3-lsp binary in @@ -52,6 +59,7 @@ your PATH, you can use the following Lua code to your Neovim configuration: vim.lsp.start { cmd = { 'openapiv3-lsp' }, filetypes = { 'yaml' }, + root_dir = vim.fn.getcwd(), } end, }) diff --git a/main.go b/main.go index fc732b5..c73773c 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,9 @@ package main import ( + "flag" + "fmt" + "io" "log" "os" @@ -9,16 +12,79 @@ import ( "github.com/armsnyder/openapiv3-lsp/internal/lsp/types" ) +// NOTE(asanyder): Version is set by release-please. +const Version = "0.1.0" + func main() { - log.SetFlags(0) + // Parse command line flags. + + var args struct { + version bool + help bool + testdata string + } + + flag.BoolVar(&args.version, "version", false, "Print the version and exit") + flag.BoolVar(&args.version, "v", false, "Print the version and exit") + flag.BoolVar(&args.help, "help", false, "Print this help message and exit") + flag.BoolVar(&args.help, "h", false, "Print this help message and exit") + flag.StringVar(&args.testdata, "testdata", "", "Capture a copy of all input and output to the specified directory. Useful for debugging or generating test data.") + + flag.Parse() + + // Handle special flags. + + if args.version { + //nolint:forbidigo // use of fmt.Println + fmt.Println(Version) + return + } + + if args.help { + flag.Usage() + return + } + + // Configure logging. + + log.SetFlags(log.Lshortfile) + + // Configure input and output. + + var reader io.Reader = os.Stdin + var writer io.Writer = os.Stdout + + if args.testdata != "" { + if err := os.MkdirAll(args.testdata, 0o755); err != nil { + log.Fatal("Failed to create testdata directory: ", err) + } + + inputFile, err := os.Create(args.testdata + "/input.jsonrpc") + if err != nil { + log.Fatal("Failed to create input file: ", err) + } + defer inputFile.Close() + + outputFile, err := os.Create(args.testdata + "/output.jsonrpc") + if err != nil { + //nolint:gocritic // exitAfterDefer + log.Fatal("Failed to create output file: ", err) + } + defer outputFile.Close() + + reader = io.TeeReader(reader, inputFile) + writer = io.MultiWriter(writer, outputFile) + } + + // Run the LSP server. server := &lsp.Server{ ServerInfo: types.ServerInfo{ Name: "openapiv3-lsp", - Version: "0.1.0", + Version: Version, }, - Reader: os.Stdin, - Writer: os.Stdout, + Reader: reader, + Writer: writer, Handler: &analysis.Handler{}, } @@ -26,3 +92,33 @@ func main() { log.Fatal("LSP server error: ", err) } } + +// shadowReader wraps a primary reader and splits the input to a secondary +// writer. +type shadowReader struct { + reader io.Reader + shadow io.Writer +} + +func (r shadowReader) Read(p []byte) (n int, err error) { + n, err = r.reader.Read(p) + _, _ = r.shadow.Write(p[:n]) + return n, err +} + +var _ io.Reader = shadowReader{} + +// shadowWriter wraps a primary writer and splits the output to a secondary +// writer. +type shadowWriter struct { + writer io.Writer + shadow io.Writer +} + +func (w shadowWriter) Write(p []byte) (n int, err error) { + n, err = w.writer.Write(p) + _, _ = w.shadow.Write(p) + return n, err +} + +var _ io.Writer = shadowWriter{}