From 19f8d2e5840c19937c60cee0c681343ab658f678 Mon Sep 17 00:00:00 2001 From: Dmitriy Matrenichev Date: Fri, 27 Sep 2024 19:10:33 +0300 Subject: [PATCH] chore: add kres Run rekres Signed-off-by: Dmitriy Matrenichev --- .codecov.yml | 18 ++ .conform.yaml | 48 ++++ .dockerignore | 9 + .github/workflows/ci.yaml | 97 ++++++++ .github/workflows/slack-notify.yaml | 92 +++++++ .gitignore | 5 + .golangci.yml | 150 +++++++++++ .license-header.go.txt | 3 + Dockerfile | 80 ++++++ LICENSE | 373 ++++++++++++++++++++++++++++ Makefile | 199 +++++++++++++++ codec/codec.go | 4 +- codec/codec_norace_test.go | 9 + codec/codec_race_test.go | 9 + codec/codec_test.go | 211 ++++++++++++---- go.mod | 8 +- go.sum | 10 - hack/release.sh | 149 +++++++++++ hack/release.toml | 11 + 19 files changed, 1424 insertions(+), 61 deletions(-) create mode 100644 .codecov.yml create mode 100644 .conform.yaml create mode 100644 .dockerignore create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/slack-notify.yaml create mode 100644 .gitignore create mode 100644 .golangci.yml create mode 100644 .license-header.go.txt create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 codec/codec_norace_test.go create mode 100644 codec/codec_race_test.go create mode 100755 hack/release.sh create mode 100644 hack/release.toml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..53fa43f --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,18 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2024-09-27T16:09:47Z by kres 8be5fa7. + +codecov: + require_ci_to_pass: false + +coverage: + status: + project: + default: + target: 50% + threshold: 0.5% + base: auto + if_ci_failed: success + patch: off + +comment: false diff --git a/.conform.yaml b/.conform.yaml new file mode 100644 index 0000000..50de8ca --- /dev/null +++ b/.conform.yaml @@ -0,0 +1,48 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2024-09-27T16:09:47Z by kres 8be5fa7. + +policies: + - type: commit + spec: + dco: true + gpg: + required: true + identity: + gitHubOrganization: siderolabs + spellcheck: + locale: US + maximumOfOneCommit: true + header: + length: 89 + imperative: true + case: lower + invalidLastCharacters: . + body: + required: true + conventional: + types: + - chore + - docs + - perf + - refactor + - style + - test + - release + scopes: + - .* + - type: license + spec: + root: . + skipPaths: + - .git/ + - testdata/ + includeSuffixes: + - .go + excludeSuffixes: + - .pb.go + - .pb.gw.go + header: | + // This Source Code Form is subject to the terms of the Mozilla Public + // License, v. 2.0. If a copy of the MPL was not distributed with this + // file, You can obtain one at http://mozilla.org/MPL/2.0/. diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..77e858f --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2024-09-27T16:09:47Z by kres 8be5fa7. + +* +!codec +!go.mod +!go.sum +!.golangci.yml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..f0cdb2f --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,97 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2024-09-27T16:09:47Z by kres 8be5fa7. + +name: default +concurrency: + group: ${{ github.head_ref || github.run_id }} + cancel-in-progress: true +"on": + push: + branches: + - main + - release-* + tags: + - v* + pull_request: + branches: + - main + - release-* +jobs: + default: + permissions: + actions: read + contents: write + issues: read + packages: write + pull-requests: read + runs-on: + - self-hosted + - generic + if: (!startsWith(github.head_ref, 'renovate/') && !startsWith(github.head_ref, 'dependabot/')) + steps: + - name: gather-system-info + id: system-info + uses: kenchan0130/actions-system-info@v1.3.0 + continue-on-error: true + - name: print-system-info + run: | + MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024)) + + OUTPUTS=( + "CPU Core: ${{ steps.system-info.outputs.cpu-core }}" + "CPU Model: ${{ steps.system-info.outputs.cpu-model }}" + "Hostname: ${{ steps.system-info.outputs.hostname }}" + "NodeName: ${NODE_NAME}" + "Kernel release: ${{ steps.system-info.outputs.kernel-release }}" + "Kernel version: ${{ steps.system-info.outputs.kernel-version }}" + "Name: ${{ steps.system-info.outputs.name }}" + "Platform: ${{ steps.system-info.outputs.platform }}" + "Release: ${{ steps.system-info.outputs.release }}" + "Total memory: ${MEMORY_GB} GB" + ) + + for OUTPUT in "${OUTPUTS[@]}";do + echo "${OUTPUT}" + done + continue-on-error: true + - name: checkout + uses: actions/checkout@v4 + - name: Unshallow + run: | + git fetch --prune --unshallow + - name: Set up Docker Buildx + id: setup-buildx + uses: docker/setup-buildx-action@v3 + with: + driver: remote + endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234 + timeout-minutes: 10 + - name: base + run: | + make base + - name: unit-tests + run: | + make unit-tests + - name: unit-tests-race + run: | + make unit-tests-race + - name: coverage + uses: codecov/codecov-action@v4 + with: + files: _out/coverage-unit-tests.txt + token: ${{ secrets.CODECOV_TOKEN }} + timeout-minutes: 3 + - name: lint + run: | + make lint + - name: release-notes + if: startsWith(github.ref, 'refs/tags/') + run: | + make release-notes + - name: Release + if: startsWith(github.ref, 'refs/tags/') + uses: crazy-max/ghaction-github-release@v2 + with: + body_path: _out/RELEASE_NOTES.md + draft: "true" diff --git a/.github/workflows/slack-notify.yaml b/.github/workflows/slack-notify.yaml new file mode 100644 index 0000000..4b6216a --- /dev/null +++ b/.github/workflows/slack-notify.yaml @@ -0,0 +1,92 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2024-09-27T16:09:47Z by kres 8be5fa7. + +name: slack-notify +"on": + workflow_run: + workflows: + - default + types: + - completed +jobs: + slack-notify: + runs-on: + - self-hosted + - generic + if: github.event.workflow_run.conclusion != 'skipped' + steps: + - name: Get PR number + id: get-pr-number + if: github.event.workflow_run.event == 'pull_request' + env: + GH_TOKEN: ${{ github.token }} + run: | + echo pull_request_number=$(gh pr view -R ${{ github.repository }} ${{ github.event.workflow_run.head_repository.owner.login }}:${{ github.event.workflow_run.head_branch }} --json number --jq .number) >> $GITHUB_OUTPUT + - name: Slack Notify + uses: slackapi/slack-github-action@v1 + with: + channel-id: proj-talos-maintainers + payload: | + { + "attachments": [ + { + "color": "${{ github.event.workflow_run.conclusion == 'success' && '#2EB886' || github.event.workflow_run.conclusion == 'failure' && '#A30002' || '#FFCC00' }}", + "fallback": "test", + "blocks": [ + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "${{ github.event.workflow_run.event == 'pull_request' && format('*Pull Request:* {0} (`{1}`)\n<{2}/pull/{3}|{4}>', github.repository, github.ref_name, github.event.repository.html_url, steps.get-pr-number.outputs.pull_request_number, github.event.workflow_run.display_title) || format('*Build:* {0} (`{1}`)\n<{2}/commit/{3}|{4}>', github.repository, github.ref_name, github.event.repository.html_url, github.sha, github.event.workflow_run.display_title) }}" + }, + { + "type": "mrkdwn", + "text": "*Status:*\n`${{ github.event.workflow_run.conclusion }}`" + } + ] + }, + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "*Author:*\n`${{ github.actor }}`" + }, + { + "type": "mrkdwn", + "text": "*Event:*\n`${{ github.event.workflow_run.event }}`" + } + ] + }, + { + "type": "divider" + }, + { + "type": "actions", + "elements": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Logs" + }, + "url": "${{ github.event.workflow_run.html_url }}" + }, + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Commit" + }, + "url": "${{ github.event.repository.html_url }}/commit/${{ github.sha }}" + } + ] + } + ] + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d6097d3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2024-09-27T16:09:47Z by kres 8be5fa7. + +_out diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..745a158 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,150 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2024-09-27T16:09:47Z by kres 8be5fa7. + +# options for analysis running +run: + timeout: 10m + issues-exit-code: 1 + tests: true + build-tags: [ ] + modules-download-mode: readonly + +# output configuration options +output: + formats: + - format: colored-line-number + path: stdout + print-issued-lines: true + print-linter-name: true + uniq-by-line: true + path-prefix: "" + +# all available settings of specific linters +linters-settings: + dogsled: + max-blank-identifiers: 2 + dupl: + threshold: 150 + errcheck: + check-type-assertions: true + check-blank: true + exhaustive: + default-signifies-exhaustive: false + gci: + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - localmodule # Imports from the same module. + gocognit: + min-complexity: 30 + nestif: + min-complexity: 5 + goconst: + min-len: 3 + min-occurrences: 3 + gocritic: + disabled-checks: [ ] + gocyclo: + min-complexity: 20 + godot: + scope: declarations + gofmt: + simplify: true + gomodguard: { } + govet: + enable-all: true + lll: + line-length: 200 + tab-width: 4 + misspell: + locale: US + ignore-words: [ ] + nakedret: + max-func-lines: 30 + prealloc: + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + nolintlint: + allow-unused: false + allow-no-explanation: [ ] + require-explanation: false + require-specific: true + rowserrcheck: { } + testpackage: { } + unparam: + check-exported: false + unused: + local-variables-are-used: false + whitespace: + multi-if: false # Enforces newlines (or comments) after every multi-line if statement + multi-func: false # Enforces newlines (or comments) after every multi-line function signature + wsl: + strict-append: true + allow-assign-and-call: true + allow-multiline-assign: true + allow-cuddle-declarations: false + allow-trailing-comment: false + force-case-trailing-whitespace: 0 + force-err-cuddling: false + allow-separated-leading-comment: false + gofumpt: + extra-rules: false + cyclop: + # the maximal code complexity to report + max-complexity: 20 + depguard: + rules: + prevent_unmaintained_packages: + list-mode: lax # allow unless explicitly denied + files: + - $all + deny: + - pkg: io/ioutil + desc: "replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil" + +linters: + enable-all: true + disable-all: false + fast: false + disable: + - exhaustruct + - err113 + - forbidigo + - funlen + - gochecknoglobals + - gochecknoinits + - godox + - gomnd + - gomoddirectives + - gosec + - inamedparam + - ireturn + - mnd + - nestif + - nonamedreturns + - paralleltest + - tagalign + - tagliatelle + - thelper + - varnamelen + - wrapcheck + - testifylint # complains about our assert recorder and has a number of false positives for assert.Greater(t, thing, 1) + - protogetter # complains about us using Value field on typed spec, instead of GetValue which has a different signature + - perfsprint # complains about us using fmt.Sprintf in non-performance critical code, updating just kres took too long + - goimports # same as gci + - musttag # seems to be broken - goes into imported libraries and reports issues there + +issues: + exclude: [ ] + exclude-rules: [ ] + exclude-use-default: false + exclude-case-sensitive: false + max-issues-per-linter: 10 + max-same-issues: 3 + new: false + +severity: + default-severity: error + case-sensitive: false diff --git a/.license-header.go.txt b/.license-header.go.txt new file mode 100644 index 0000000..66e0819 --- /dev/null +++ b/.license-header.go.txt @@ -0,0 +1,3 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fb6d5f2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,80 @@ +# syntax = docker/dockerfile-upstream:1.10.0-labs + +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2024-10-02T14:07:05Z by kres 34e72ac. + +ARG TOOLCHAIN + +# cleaned up specs and compiled versions +FROM scratch AS generate + +# base toolchain image +FROM --platform=${BUILDPLATFORM} ${TOOLCHAIN} AS toolchain +RUN apk --update --no-cache add bash curl build-base protoc protobuf-dev + +# build tools +FROM --platform=${BUILDPLATFORM} toolchain AS tools +ENV GO111MODULE=on +ARG CGO_ENABLED +ENV CGO_ENABLED=${CGO_ENABLED} +ARG GOTOOLCHAIN +ENV GOTOOLCHAIN=${GOTOOLCHAIN} +ARG GOEXPERIMENT +ENV GOEXPERIMENT=${GOEXPERIMENT} +ENV GOPATH=/go +ARG DEEPCOPY_VERSION +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install github.com/siderolabs/deep-copy@${DEEPCOPY_VERSION} \ + && mv /go/bin/deep-copy /bin/deep-copy +ARG GOLANGCILINT_VERSION +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install github.com/golangci/golangci-lint/cmd/golangci-lint@${GOLANGCILINT_VERSION} \ + && mv /go/bin/golangci-lint /bin/golangci-lint +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg go install golang.org/x/vuln/cmd/govulncheck@latest \ + && mv /go/bin/govulncheck /bin/govulncheck +ARG GOFUMPT_VERSION +RUN go install mvdan.cc/gofumpt@${GOFUMPT_VERSION} \ + && mv /go/bin/gofumpt /bin/gofumpt + +# tools and sources +FROM tools AS base +WORKDIR /src +COPY go.mod go.mod +COPY go.sum go.sum +RUN cd . +RUN --mount=type=cache,target=/go/pkg go mod download +RUN --mount=type=cache,target=/go/pkg go mod verify +COPY ./codec ./codec +RUN --mount=type=cache,target=/go/pkg go list -mod=readonly all >/dev/null + +# runs gofumpt +FROM base AS lint-gofumpt +RUN FILES="$(gofumpt -l .)" && test -z "${FILES}" || (echo -e "Source code is not formatted with 'gofumpt -w .':\n${FILES}"; exit 1) + +# runs golangci-lint +FROM base AS lint-golangci-lint +WORKDIR /src +COPY .golangci.yml . +ENV GOGC=50 +RUN golangci-lint config verify --config .golangci.yml +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/root/.cache/golangci-lint --mount=type=cache,target=/go/pkg golangci-lint run --config .golangci.yml + +# runs govulncheck +FROM base AS lint-govulncheck +WORKDIR /src +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg govulncheck ./... + +# runs unit-tests with race detector +FROM base AS unit-tests-race +WORKDIR /src +ARG TESTPKGS +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg --mount=type=cache,target=/tmp CGO_ENABLED=1 go test -v -race -count 1 ${TESTPKGS} + +# runs unit-tests +FROM base AS unit-tests-run +WORKDIR /src +ARG TESTPKGS +RUN --mount=type=cache,target=/root/.cache/go-build --mount=type=cache,target=/go/pkg --mount=type=cache,target=/tmp go test -v -covermode=atomic -coverprofile=coverage.txt -coverpkg=${TESTPKGS} -count 1 ${TESTPKGS} + +FROM scratch AS unit-tests +COPY --from=unit-tests-run /src/coverage.txt /coverage-unit-tests.txt + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a612ad9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7f85f05 --- /dev/null +++ b/Makefile @@ -0,0 +1,199 @@ +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2024-10-02T14:07:05Z by kres 34e72ac. + +# common variables + +SHA := $(shell git describe --match=none --always --abbrev=8 --dirty) +TAG := $(shell git describe --tag --always --dirty --match v[0-9]\*) +ABBREV_TAG := $(shell git describe --tags >/dev/null 2>/dev/null && git describe --tag --always --match v[0-9]\* --abbrev=0 || echo 'undefined') +BRANCH := $(shell git rev-parse --abbrev-ref HEAD) +ARTIFACTS := _out +IMAGE_TAG ?= $(TAG) +OPERATING_SYSTEM := $(shell uname -s | tr '[:upper:]' '[:lower:]') +GOARCH := $(shell uname -m | sed 's/x86_64/amd64/' | sed 's/aarch64/arm64/') +WITH_DEBUG ?= false +WITH_RACE ?= false +REGISTRY ?= ghcr.io +USERNAME ?= siderolabs +REGISTRY_AND_USERNAME ?= $(REGISTRY)/$(USERNAME) +PROTOBUF_GO_VERSION ?= 1.34.2 +GRPC_GO_VERSION ?= 1.5.1 +GRPC_GATEWAY_VERSION ?= 2.22.0 +VTPROTOBUF_VERSION ?= 0.6.0 +GOIMPORTS_VERSION ?= 0.25.0 +DEEPCOPY_VERSION ?= v0.5.6 +GOLANGCILINT_VERSION ?= v1.61.0 +GOFUMPT_VERSION ?= v0.7.0 +GO_VERSION ?= 1.23.2 +GO_BUILDFLAGS ?= +GO_LDFLAGS ?= +CGO_ENABLED ?= 0 +GOTOOLCHAIN ?= local +TESTPKGS ?= ./... +KRES_IMAGE ?= ghcr.io/siderolabs/kres:latest +CONFORMANCE_IMAGE ?= ghcr.io/siderolabs/conform:latest + +# docker build settings + +BUILD := docker buildx build +PLATFORM ?= linux/amd64 +PROGRESS ?= auto +PUSH ?= false +CI_ARGS ?= +COMMON_ARGS = --file=Dockerfile +COMMON_ARGS += --provenance=false +COMMON_ARGS += --progress=$(PROGRESS) +COMMON_ARGS += --platform=$(PLATFORM) +COMMON_ARGS += --push=$(PUSH) +COMMON_ARGS += --build-arg=ARTIFACTS="$(ARTIFACTS)" +COMMON_ARGS += --build-arg=SHA="$(SHA)" +COMMON_ARGS += --build-arg=TAG="$(TAG)" +COMMON_ARGS += --build-arg=ABBREV_TAG="$(ABBREV_TAG)" +COMMON_ARGS += --build-arg=USERNAME="$(USERNAME)" +COMMON_ARGS += --build-arg=REGISTRY="$(REGISTRY)" +COMMON_ARGS += --build-arg=TOOLCHAIN="$(TOOLCHAIN)" +COMMON_ARGS += --build-arg=CGO_ENABLED="$(CGO_ENABLED)" +COMMON_ARGS += --build-arg=GO_BUILDFLAGS="$(GO_BUILDFLAGS)" +COMMON_ARGS += --build-arg=GO_LDFLAGS="$(GO_LDFLAGS)" +COMMON_ARGS += --build-arg=GOTOOLCHAIN="$(GOTOOLCHAIN)" +COMMON_ARGS += --build-arg=GOEXPERIMENT="$(GOEXPERIMENT)" +COMMON_ARGS += --build-arg=PROTOBUF_GO_VERSION="$(PROTOBUF_GO_VERSION)" +COMMON_ARGS += --build-arg=GRPC_GO_VERSION="$(GRPC_GO_VERSION)" +COMMON_ARGS += --build-arg=GRPC_GATEWAY_VERSION="$(GRPC_GATEWAY_VERSION)" +COMMON_ARGS += --build-arg=VTPROTOBUF_VERSION="$(VTPROTOBUF_VERSION)" +COMMON_ARGS += --build-arg=GOIMPORTS_VERSION="$(GOIMPORTS_VERSION)" +COMMON_ARGS += --build-arg=DEEPCOPY_VERSION="$(DEEPCOPY_VERSION)" +COMMON_ARGS += --build-arg=GOLANGCILINT_VERSION="$(GOLANGCILINT_VERSION)" +COMMON_ARGS += --build-arg=GOFUMPT_VERSION="$(GOFUMPT_VERSION)" +COMMON_ARGS += --build-arg=TESTPKGS="$(TESTPKGS)" +TOOLCHAIN ?= docker.io/golang:1.23-alpine + +# help menu + +export define HELP_MENU_HEADER +# Getting Started + +To build this project, you must have the following installed: + +- git +- make +- docker (19.03 or higher) + +## Creating a Builder Instance + +The build process makes use of experimental Docker features (buildx). +To enable experimental features, add 'experimental: "true"' to '/etc/docker/daemon.json' on +Linux or enable experimental features in Docker GUI for Windows or Mac. + +To create a builder instance, run: + + docker buildx create --name local --use + +If running builds that needs to be cached aggresively create a builder instance with the following: + + docker buildx create --name local --use --config=config.toml + +config.toml contents: + +[worker.oci] + gc = true + gckeepstorage = 50000 + + [[worker.oci.gcpolicy]] + keepBytes = 10737418240 + keepDuration = 604800 + filters = [ "type==source.local", "type==exec.cachemount", "type==source.git.checkout"] + [[worker.oci.gcpolicy]] + all = true + keepBytes = 53687091200 + +If you already have a compatible builder instance, you may use that instead. + +## Artifacts + +All artifacts will be output to ./$(ARTIFACTS). Images will be tagged with the +registry "$(REGISTRY)", username "$(USERNAME)", and a dynamic tag (e.g. $(IMAGE):$(IMAGE_TAG)). +The registry and username can be overridden by exporting REGISTRY, and USERNAME +respectively. + +endef + +ifneq (, $(filter $(WITH_RACE), t true TRUE y yes 1)) +GO_BUILDFLAGS += -race +CGO_ENABLED := 1 +GO_LDFLAGS += -linkmode=external -extldflags '-static' +endif + +ifneq (, $(filter $(WITH_DEBUG), t true TRUE y yes 1)) +GO_BUILDFLAGS += -tags sidero.debug +else +GO_LDFLAGS += -s +endif + +all: unit-tests lint + +$(ARTIFACTS): ## Creates artifacts directory. + @mkdir -p $(ARTIFACTS) + +.PHONY: clean +clean: ## Cleans up all artifacts. + @rm -rf $(ARTIFACTS) + +target-%: ## Builds the specified target defined in the Dockerfile. The build result will only remain in the build cache. + @$(BUILD) --target=$* $(COMMON_ARGS) $(TARGET_ARGS) $(CI_ARGS) . + +local-%: ## Builds the specified target defined in the Dockerfile using the local output type. The build result will be output to the specified local destination. + @$(MAKE) target-$* TARGET_ARGS="--output=type=local,dest=$(DEST) $(TARGET_ARGS)" + +lint-golangci-lint: ## Runs golangci-lint linter. + @$(MAKE) target-$@ + +lint-gofumpt: ## Runs gofumpt linter. + @$(MAKE) target-$@ + +.PHONY: fmt +fmt: ## Formats the source code + @docker run --rm -it -v $(PWD):/src -w /src golang:$(GO_VERSION) \ + bash -c "export GOTOOLCHAIN=local; \ + export GO111MODULE=on; export GOPROXY=https://proxy.golang.org; \ + go install mvdan.cc/gofumpt@$(GOFUMPT_VERSION) && \ + gofumpt -w ." + +lint-govulncheck: ## Runs govulncheck linter. + @$(MAKE) target-$@ + +.PHONY: base +base: ## Prepare base toolchain + @$(MAKE) target-$@ + +.PHONY: unit-tests +unit-tests: ## Performs unit tests + @$(MAKE) local-$@ DEST=$(ARTIFACTS) + +.PHONY: unit-tests-race +unit-tests-race: ## Performs unit tests with race detection enabled. + @$(MAKE) target-$@ + +.PHONY: lint +lint: lint-golangci-lint lint-gofumpt lint-govulncheck ## Run all linters for the project. + +.PHONY: rekres +rekres: + @docker pull $(KRES_IMAGE) + @docker run --rm --net=host --user $(shell id -u):$(shell id -g) -v $(PWD):/src -w /src -e GITHUB_TOKEN $(KRES_IMAGE) + +.PHONY: help +help: ## This help menu. + @echo "$$HELP_MENU_HEADER" + @grep -E '^[a-zA-Z%_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + +.PHONY: release-notes +release-notes: $(ARTIFACTS) + @ARTIFACTS=$(ARTIFACTS) ./hack/release.sh $@ $(ARTIFACTS)/RELEASE_NOTES.md $(TAG) + +.PHONY: conformance +conformance: + @docker pull $(CONFORMANCE_IMAGE) + @docker run --rm -it -v $(PWD):/src -w /src $(CONFORMANCE_IMAGE) enforce + diff --git a/codec/codec.go b/codec/codec.go index c8d50f1..5490999 100644 --- a/codec/codec.go +++ b/codec/codec.go @@ -35,7 +35,7 @@ func (vtprotoCodec) Marshal(v any) (mem.BufferSlice, error) { pool := mem.DefaultBufferPool() buf := pool.Get(size) - if err := marshalAppend((*buf)[:size], v); err != nil { + if err := marshalAppend(*buf, v); err != nil { pool.Put(buf) return nil, err @@ -77,7 +77,7 @@ func marshalAppend(dst []byte, v any) error { case vtprotoMessage: return takeErr(v.MarshalToSizedBufferVT(dst)) case gproto.Message: - return takeErr((gproto.MarshalOptions{}).MarshalAppend(dst, v)) + return takeErr((gproto.MarshalOptions{}).MarshalAppend(dst[:0], v)) case protoadapt.MessageV1: return takeErr((gproto.MarshalOptions{}).MarshalAppend(dst[:0], protoadapt.MessageV2Of(v))) default: diff --git a/codec/codec_norace_test.go b/codec/codec_norace_test.go new file mode 100644 index 0000000..4c75060 --- /dev/null +++ b/codec/codec_norace_test.go @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//go:build !race + +package codec_test + +const allocsCount = 3 diff --git a/codec/codec_race_test.go b/codec/codec_race_test.go new file mode 100644 index 0000000..a0a7d79 --- /dev/null +++ b/codec/codec_race_test.go @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +//go:build race + +package codec_test + +const allocsCount = 4 diff --git a/codec/codec_test.go b/codec/codec_test.go index 5fd4a22..f5f97e9 100644 --- a/codec/codec_test.go +++ b/codec/codec_test.go @@ -5,93 +5,220 @@ package codec_test import ( + "strings" "testing" - "time" - vtimestamppb "github.com/planetscale/vtprotobuf/types/known/timestamppb" + vtwrapperspb "github.com/planetscale/vtprotobuf/types/known/wrapperspb" "google.golang.org/grpc/encoding" - "google.golang.org/protobuf/types/known/timestamppb" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/runtime/protoimpl" + "google.golang.org/protobuf/types/known/wrapperspb" ) -func TestProtobuf(t *testing.T) { - expected := time.Now().UTC() +func testProtobuf[T any](t testing.TB, what *T, check func(testing.TB, *T)) { + c := encoding.GetCodecV2("proto") + + out, err := c.Marshal(what) + if err != nil { + t.Fatal(err) + } - testProtobuf(t, timestamppb.New(expected), check(expected)) - res := testing.Benchmark(BenchmarkProtobuf) + defer out.Free() - if allocs := res.AllocsPerOp(); allocs != 4 { - t.Fatalf("unexpected number of allocations: %d", allocs) + var result T + + err = c.Unmarshal(out, &result) + if err != nil { + t.Fatal(err) } + + check(t, &result) } -func BenchmarkProtobuf(b *testing.B) { - expected := time.Now().UTC() - ts := timestamppb.New(expected) - c := check(expected) +type testData struct { + length int + allocs int64 +} + +func TestProtobuf(t *testing.T) { + tests := map[string]testData{ + "short string": { + length: 42, + allocs: 5, + }, + "long string": { + length: 10240, + allocs: allocsCount, + }, + } + + for name, d := range tests { + if !t.Run(name, func(t *testing.T) { + str := generateString(d.length) + value := wrapperspb.String(str) + c := check(str) - b.ReportAllocs() - b.ResetTimer() + testProtobuf(t, value, c) - for range b.N { - testProtobuf(b, ts, c) + res := testing.Benchmark(benchmarkProtobuf(func(t testing.TB) { testProtobuf(t, value, c) })) + + if allocs := res.AllocsPerOp(); d.allocs != allocs { + t.Fatalf("unexpected number of allocations: expected %d != actual %d", d.allocs, allocs) + } + }) { + break + } } } -func check(expected time.Time) func(t testing.TB, what *timestamppb.Timestamp) { - return func(t testing.TB, what *timestamppb.Timestamp) { - if !expected.Equal(what.AsTime()) { - t.Fatal("timestamps are not equal") +func BenchmarkProtobuf(b *testing.B) { + str := generateString(42) + value := wrapperspb.String(str) + c := check(str) + + benchmarkProtobuf(func(t testing.TB) { testProtobuf(t, value, c) })(b) +} + +func check(expected string) func(t testing.TB, what *wrapperspb.StringValue) { + return func(t testing.TB, what *wrapperspb.StringValue) { + if expected != what.GetValue() { + t.Fatal("strings are not equal", expected, what.GetValue()) } } } func TestVTProtobuf(t *testing.T) { - expected := time.Now().UTC() + tests := map[string]testData{ + "short string": { + length: 42, + allocs: 5, + }, + "long string": { + length: 10240, + allocs: allocsCount, + }, + } + + for name, d := range tests { + if !t.Run(name, func(t *testing.T) { + str := generateString(d.length) + value := (*vtwrapperspb.StringValue)(wrapperspb.String(str)) + c := checkVT(str) - testProtobuf(t, (*vtimestamppb.Timestamp)(timestamppb.New(expected)), checkVT(expected)) + testProtobuf(t, value, c) - res := testing.Benchmark(BenchmarkVTProtobuf) + res := testing.Benchmark(benchmarkProtobuf(func(t testing.TB) { testProtobuf(t, value, c) })) - if allocs := res.AllocsPerOp(); allocs != 4 { - t.Fatalf("unexpected number of allocations: %d", allocs) + if allocs := res.AllocsPerOp(); d.allocs != allocs { + t.Fatalf("unexpected number of allocations: expected %d != actual %d", d.allocs, allocs) + } + }) { + break + } } } func BenchmarkVTProtobuf(b *testing.B) { - expected := time.Now().UTC() - ts := (*vtimestamppb.Timestamp)(timestamppb.New(expected)) - c := checkVT(expected) + str := generateString(10240) + value := (*vtwrapperspb.StringValue)(wrapperspb.String(str)) + c := checkVT(str) - b.ReportAllocs() - b.ResetTimer() + benchmarkProtobuf(func(t testing.TB) { testProtobuf(t, value, c) })(b) +} - for range b.N { - testProtobuf(b, ts, c) +func checkVT(expected string) func(t testing.TB, what *vtwrapperspb.StringValue) { + return func(t testing.TB, what *vtwrapperspb.StringValue) { + if expected != what.Value { + t.Fatal("strings are not equal", expected, what.Value) + } } } -func checkVT(expected time.Time) func(t testing.TB, what *vtimestamppb.Timestamp) { - return func(t testing.TB, what *vtimestamppb.Timestamp) { - if !expected.Equal((*timestamppb.Timestamp)(what).AsTime()) { - t.Fatal("timestamps are not equal", expected, (*timestamppb.Timestamp)(what).AsTime()) +func TestOldProtobuf(t *testing.T) { + tests := map[string]testData{ + "short string": { + length: 42, + allocs: 8, + }, + "long string": { + length: 10240, + allocs: 10, + }, + } + + for name, d := range tests { + if !t.Run(name, func(t *testing.T) { + str := generateString(d.length) + value := wrapperspb.String(str) + c := check(str) + + testProtobufOld(t, value, c) + + res := testing.Benchmark(benchmarkProtobuf(func(t testing.TB) { testProtobufOld(t, value, c) })) + + if allocs := res.AllocsPerOp(); d.allocs != allocs { + t.Fatalf("unexpected number of allocations: expected %d != actual %d", d.allocs, allocs) + } + }) { + break } } } -func testProtobuf[T any](t testing.TB, what *T, check func(testing.TB, *T)) { +func BenchmarkOldProtobuf(b *testing.B) { + str := generateString(42) + value := wrapperspb.String(str) + c := check(str) + + benchmarkProtobuf(func(t testing.TB) { testProtobufOld(t, value, c) })(b) +} + +func testProtobufOld(t testing.TB, what *wrapperspb.StringValue, check func(testing.TB, *wrapperspb.StringValue)) { c := encoding.GetCodecV2("proto") - out, err := c.Marshal(what) + out, err := c.Marshal((*oldProto)(what)) if err != nil { t.Fatal(err) } - var result T + rssult := &wrapperspb.StringValue{} - err = c.Unmarshal(out, &result) + err = c.Unmarshal(out, (*oldProto)(rssult)) if err != nil { t.Fatal(err) } - check(t, &result) + check(t, rssult) +} + +// Because all protobuf types provide V2 version of methods, we need to manually cast it down to V1. +type oldProto wrapperspb.StringValue + +func (x *oldProto) Reset() { (*wrapperspb.StringValue)(x).Reset() } + +func (x *oldProto) String() string { return messageString((*wrapperspb.StringValue)(x)) } +func (*oldProto) ProtoMessage() {} +func messageString(m protoreflect.ProtoMessage) string { + return protoimpl.X.MessageStringOf(m) +} + +func generateString(length int) string { + var builder strings.Builder + + for i := range length { + builder.WriteRune(rune('a' + i%26)) + } + + return builder.String() +} + +func benchmarkProtobuf(fn func(t testing.TB)) func(b *testing.B) { + return func(b *testing.B) { + b.ReportAllocs() + b.ResetTimer() + + for range b.N { + fn(b) + } + } } diff --git a/go.mod b/go.mod index c662f8c..84c2cbf 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,8 @@ go 1.23.0 require ( github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587 - github.com/stretchr/testify v1.9.0 google.golang.org/grpc v1.67.0 google.golang.org/protobuf v1.34.2 ) -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.25.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require golang.org/x/sys v0.25.0 // indirect diff --git a/go.sum b/go.sum index 3acc177..6553456 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,7 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587 h1:xzZOeCMQLA/W198ZkdVdt4EKFKJtS26B773zNU377ZY= github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= @@ -20,7 +14,3 @@ google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -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.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/hack/release.sh b/hack/release.sh new file mode 100755 index 0000000..72d2ebf --- /dev/null +++ b/hack/release.sh @@ -0,0 +1,149 @@ +#!/usr/bin/env bash + +# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT. +# +# Generated on 2024-09-27T16:09:47Z by kres 8be5fa7. + +set -e + +RELEASE_TOOL_IMAGE="ghcr.io/siderolabs/release-tool:latest" + +function release-tool { + docker pull "${RELEASE_TOOL_IMAGE}" >/dev/null + docker run --rm -w /src -v "${PWD}":/src:ro "${RELEASE_TOOL_IMAGE}" -l -d -n -t "${1}" ./hack/release.toml +} + +function changelog { + if [ "$#" -eq 1 ]; then + (release-tool ${1}; echo; cat CHANGELOG.md) > CHANGELOG.md- && mv CHANGELOG.md- CHANGELOG.md + else + echo 1>&2 "Usage: $0 changelog [tag]" + exit 1 + fi +} + +function release-notes { + release-tool "${2}" > "${1}" +} + +function cherry-pick { + if [ $# -ne 2 ]; then + echo 1>&2 "Usage: $0 cherry-pick " + exit 1 + fi + + git checkout $2 + git fetch + git rebase upstream/$2 + git cherry-pick -x $1 +} + +function commit { + if [ $# -ne 1 ]; then + echo 1>&2 "Usage: $0 commit " + exit 1 + fi + + if is_on_main_branch; then + update_license_files + fi + + git commit -s -m "release($1): prepare release" -m "This is the official $1 release." +} + +function is_on_main_branch { + main_remotes=("upstream" "origin") + branch_names=("main" "master") + current_branch=$(git rev-parse --abbrev-ref HEAD) + + echo "Check current branch: $current_branch" + + for remote in "${main_remotes[@]}"; do + echo "Fetch remote $remote..." + + if ! git fetch --quiet "$remote" &>/dev/null; then + echo "Failed to fetch $remote, skip..." + + continue + fi + + for branch_name in "${branch_names[@]}"; do + if ! git rev-parse --verify "$branch_name" &>/dev/null; then + echo "Branch $branch_name does not exist, skip..." + + continue + fi + + echo "Branch $remote/$branch_name exists, comparing..." + + merge_base=$(git merge-base "$current_branch" "$remote/$branch_name") + latest_main=$(git rev-parse "$remote/$branch_name") + + if [ "$merge_base" = "$latest_main" ]; then + echo "Current branch is up-to-date with $remote/$branch_name" + + return 0 + else + echo "Current branch is not on $remote/$branch_name" + + return 1 + fi + done + done + + echo "No main or master branch found on any remote" + + return 1 +} + +function update_license_files { + script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + parent_dir="$(dirname "$script_dir")" + current_year=$(date +"%Y") + change_date=$(date -v+4y +"%Y-%m-%d" 2>/dev/null || date -d "+4 years" +"%Y-%m-%d" 2>/dev/null || date --date="+4 years" +"%Y-%m-%d") + + # Find LICENSE and .kres.yaml files recursively in the parent directory (project root) + find "$parent_dir" \( -name "LICENSE" -o -name ".kres.yaml" \) -type f | while read -r file; do + temp_file="${file}.tmp" + + if [[ $file == *"LICENSE" ]]; then + if grep -q "^Business Source License" "$file"; then + sed -e "s/The Licensed Work is (c) [0-9]\{4\}/The Licensed Work is (c) $current_year/" \ + -e "s/Change Date: [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}/Change Date: $change_date/" \ + "$file" >"$temp_file" + else + continue # Not a Business Source License file + fi + elif [[ $file == *".kres.yaml" ]]; then + sed -E 's/^([[:space:]]*)ChangeDate:.*$/\1ChangeDate: "'"$change_date"'"/' "$file" >"$temp_file" + fi + + # Check if the file has changed + if ! cmp -s "$file" "$temp_file"; then + mv "$temp_file" "$file" + echo "Updated: $file" + git add "$file" + else + echo "No changes: $file" + rm "$temp_file" + fi + done +} + +if declare -f "$1" > /dev/null +then + cmd="$1" + shift + $cmd "$@" +else + cat <