diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index e48d4303f1..577198d399 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -15,4 +15,4 @@ updates:
labels:
- "dependencies"
commit-message:
- prefix: "bot"
\ No newline at end of file
+ prefix: "bot"
diff --git a/.github/workflows/build-dependencies.yml b/.github/workflows/build-dependencies.yml
index 112f847192..0bed67de09 100644
--- a/.github/workflows/build-dependencies.yml
+++ b/.github/workflows/build-dependencies.yml
@@ -37,7 +37,7 @@ jobs:
- name: Setup Go environment explicitly
uses: actions/setup-go@v3
with:
- go-version: "1.20"
+ go-version: "1.21"
check-latest: true
- name: Build all dependencies
diff --git a/.github/workflows/build-then-deploy-ami.yml b/.github/workflows/build-then-deploy-ami.yml
index ce6be1e0bd..4423d70659 100644
--- a/.github/workflows/build-then-deploy-ami.yml
+++ b/.github/workflows/build-then-deploy-ami.yml
@@ -109,7 +109,7 @@ jobs:
- name: Terraform validation
run: terraform validate -no-color
- - name: List workspaces
+ - name: List workspaces
run: ls workspaces
- name: Terraform Apply
diff --git a/.github/workflows/check-vulnerabilities.yml b/.github/workflows/check-vulnerabilities.yml
index 18e5f60de8..67d806ab6e 100644
--- a/.github/workflows/check-vulnerabilities.yml
+++ b/.github/workflows/check-vulnerabilities.yml
@@ -33,7 +33,7 @@ jobs:
- name: Run govulncheck
uses: golang/govulncheck-action@v1
with:
- go-version-input: "1.20"
+ go-version-input: "1.21"
go-package: ./...
check-latest: true
cache: true
diff --git a/.github/workflows/combine-bot-prs.yml b/.github/workflows/combine-bot-prs.yml
index 59a7910ced..abe21143d8 100644
--- a/.github/workflows/combine-bot-prs.yml
+++ b/.github/workflows/combine-bot-prs.yml
@@ -22,10 +22,10 @@ on:
default: 'dependabot'
mustBeGreen:
- description: 'Only combine PRs that are green (status is success). Set to false if repo does not run checks'
+ description: 'Only combine PRs that are green (status is success). Keep false if repo does not run checks'
type: boolean
required: true
- default: true
+ default: false
combineBranchName:
description: 'Name of the branch to combine PRs into'
@@ -35,7 +35,7 @@ on:
ignoreLabel:
description: 'Exclude PRs with this label'
required: true
- default: 'nocombine'
+ default: 'DO NOT MERGE'
jobs:
combine-bot-prs:
@@ -44,12 +44,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/github-script@v6
+ - name: Set current date as env variable
+ run: echo "CURRENT_DATE=$(date +'%d-%m-%Y')" >> ${GITHUB_ENV}
+ - name: Create combined pr
id: create-combined-pr
-
- name: Create combined pr
-
+ uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
@@ -66,7 +66,7 @@ jobs:
if (branch.startsWith('${{ github.event.inputs.branchPrefix }}')) {
console.log('Branch matched prefix: ' + branch);
let statusOK = true;
- if(${{ github.event.inputs.mustBeGreen }}) {
+ if (${{ github.event.inputs.mustBeGreen }}) {
console.log('Checking green status: ' + branch);
const stateQuery = `query($owner: String!, $repo: String!, $pull_number: Int!) {
repository(owner: $owner, name: $repo) {
@@ -92,17 +92,17 @@ jobs:
const [{ commit }] = result.repository.pullRequest.commits.nodes;
const state = commit.statusCheckRollup.state
console.log('Validating status: ' + state);
- if(state != 'SUCCESS') {
+ if (state != 'SUCCESS') {
console.log('Discarding ' + branch + ' with status ' + state);
statusOK = false;
}
}
console.log('Checking labels: ' + branch);
const labels = pull['labels'];
- for(const label of labels) {
+ for (const label of labels) {
const labelName = label['name'];
console.log('Checking label: ' + labelName);
- if(labelName == '${{ github.event.inputs.ignoreLabel }}') {
+ if (labelName == '${{ github.event.inputs.ignoreLabel }}') {
console.log('Discarding ' + branch + ' with label ' + labelName);
statusOK = false;
}
@@ -110,7 +110,10 @@ jobs:
if (statusOK) {
console.log('Adding branch to array: ' + branch);
const prString = '#' + pull['number'] + ' ' + pull['title'];
- branchesAndPRStrings.push({ branch, prString });
+ branchesAndPRStrings.push({
+ branch,
+ prString
+ });
baseBranch = pull['base']['ref'];
baseBranchSHA = pull['base']['sha'];
}
@@ -135,7 +138,7 @@ jobs:
let combinedPRs = [];
let mergeFailedPRs = [];
- for(const { branch, prString } of branchesAndPRStrings) {
+ for (const { branch, prString } of branchesAndPRStrings) {
try {
await github.rest.repos.merge({
owner: context.repo.owner,
@@ -153,15 +156,15 @@ jobs:
console.log('Creating combined PR');
const combinedPRsString = combinedPRs.join('\n');
- let body = 'â
This PR was created by the Combine PRs action by combining the following PRs:\n' + combinedPRsString;
- if(mergeFailedPRs.length > 0) {
+ let body = 'â
This PR was created by combining the following PRs:\n' + combinedPRsString;
+ if (mergeFailedPRs.length > 0) {
const mergeFailedPRsString = mergeFailedPRs.join('\n');
body += '\n\nâ ď¸ The following PRs were left out due to merge conflicts:\n' + mergeFailedPRsString
}
await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
- title: 'bot: Combined PRs',
+ title: 'bot: Update dependencies (bulk dependabot PRs) ${CURRENT_DATE}',
head: '${{ github.event.inputs.combineBranchName }}',
base: baseBranch,
body: body
diff --git a/.github/workflows/lint-then-benchmark.yml b/.github/workflows/lint-then-benchmark.yml
index 015c8725c2..c984cce3ef 100644
--- a/.github/workflows/lint-then-benchmark.yml
+++ b/.github/workflows/lint-then-benchmark.yml
@@ -57,7 +57,7 @@ jobs:
- name: Setup Go environment explicitly
uses: actions/setup-go@v3
with:
- go-version: "1.20"
+ go-version: "1.21"
check-latest: true
- name: Run the golangci-lint
@@ -270,7 +270,8 @@ jobs:
github.event_name == 'pull_request' &&
github.base_ref == 'develop'
run: >
- ${GOPATH}/bin/benchstat -html -alpha 1.1 develop.txt current.txt | sed -n "/
/,/<\/body>/p" > comparison.html &&
+ ${GOPATH}/bin/benchstat -html -alpha 1.1 develop.txt current.txt |
+ sed -n "//,/<\/body>/p" > comparison.html &&
./tools/scripts/pretty-benchstat-html.sh comparison.html > pretty-comparison.md
- name: Comment Benchmark Results on PR
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index a08db5565a..b482903cad 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -24,8 +24,8 @@ permissions:
contents: read
jobs:
- lint:
- name: Lint job
+ lint-go:
+ name: Lint GoLang job
runs-on: ubuntu-latest
@@ -36,12 +36,11 @@ jobs:
- name: Setup Go environment explicitly
uses: actions/setup-go@v3
with:
- go-version: "1.20"
+ go-version: "1.21"
check-latest: true
- - name: Check linting through golangci-lint
+ - name: Run golangci-lint linter
uses: golangci/golangci-lint-action@v3
-
with:
# Required: the version of golangci-lint is required.
# Note: The version should not pick the patch version as the latest patch
@@ -68,3 +67,18 @@ jobs:
# anyways so there shouldn't be any linter errors anyways. The enforces us to
# always have a clean lint state.
only-new-issues: false
+
+ lint-yaml:
+ name: Lint YAML job
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code into the directory
+ uses: actions/checkout@v3
+
+ - name: Run yamllint linter
+ uses: ibiqlik/action-yamllint@v3
+ with:
+ config_file: tools/configs/yamllint.yaml
+ file_or_dir: .
diff --git a/.github/workflows/preview-ami-with-terraform-plan.yml b/.github/workflows/preview-ami-with-terraform-plan.yml
index ed2fef6f0c..25e975a247 100644
--- a/.github/workflows/preview-ami-with-terraform-plan.yml
+++ b/.github/workflows/preview-ami-with-terraform-plan.yml
@@ -131,5 +131,5 @@ jobs:
if: steps.terraform-plan.outcome == 'failure'
run: exit 1
- - name: List workspaces
+ - name: List workspaces
run: ls workspaces
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index bfcdf9666f..6dd1a0ec00 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -28,79 +28,88 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
+
steps:
- name: Checkout code into the directory
uses: actions/checkout@v3
with:
fetch-depth: 0
+ - name: Setup Go environment explicitly
+ uses: actions/setup-go@v3
+ with:
+ go-version: "1.21"
+ check-latest: true
+ cache: true
+
- name: Apply tag
run: git tag ${{ github.event.inputs.tag }}
-
+
- name: Build modules
run: make deps:modules
-
+
- name: Set up QEMU
- uses: docker/setup-qemu-action@v2
if: matrix.os == 'ubuntu-latest'
+ uses: docker/setup-qemu-action@v2
- name: Log in to Docker Hub
- uses: docker/login-action@v2
if: matrix.os == 'ubuntu-latest'
+ uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Log in to the Container registry
- uses: docker/login-action@v2
if: matrix.os == 'ubuntu-latest'
+ uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Setup Go environment explicitly
- uses: actions/setup-go@v3
+
+ - name: Run command to get SHA environment
+ shell: bash
+ run: echo "sha_short=$(git rev-parse --short HEAD)" >> ${GITHUB_ENV}
+
+ - name: Run GoReleaser
+ uses: goreleaser/goreleaser-action@v5
with:
- go-version: "1.20"
- check-latest: true
- cache: true
+ distribution: goreleaser-pro
+ version: latest
+ args: release --clean --split ${{ env.flags }}
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GITHUB_REPOSITORY: ${{ github.repository }}
+ GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
- - shell: bash
- run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- - uses: actions/cache@v4
+ - name: Save cache on Linux
if: matrix.os == 'ubuntu-latest'
+ uses: actions/cache/save@v4
with:
path: dist/linux_amd64
key: linux-${{ env.sha_short }}
- - uses: actions/cache@v4
+
+ - name: Save cache on MacOS
if: matrix.os == 'macos-latest'
+ uses: actions/cache/save@v4
with:
path: dist/darwin_amd64
key: darwin-${{ env.sha_short }}
- - uses: actions/cache@v4
+
+ - name: Save cache on Windows
if: matrix.os == 'windows-latest'
+ uses: actions/cache/save@v4
with:
path: dist/windows_amd64
key: windows-${{ env.sha_short }}
enableCrossOsArchive: true
- - name: Run GoReleaser
- uses: goreleaser/goreleaser-action@v5
- with:
- distribution: goreleaser-pro
- version: latest
- args: release --clean --split ${{ env.flags }}
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GITHUB_REPOSITORY: ${{ github.repository }}
- GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
-
release:
runs-on: ubuntu-latest
needs: prepare
steps:
- - uses: actions/checkout@v3
+ - name: Checkout code into the directory
+ uses: actions/checkout@v3
with:
fetch-depth: 0
@@ -110,37 +119,57 @@ jobs:
- name: Setup Go environment explicitly
uses: actions/setup-go@v3
with:
- go-version: "1.20"
+ go-version: "1.21"
check-latest: true
cache: true
-
+
- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- # copy the cashes from prepare
- - shell: bash
- run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
- - uses: actions/cache@v4
+ - name: Run command to get SHA environment
+ shell: bash
+ run: echo "sha_short=$(git rev-parse --short HEAD)" >> ${GITHUB_ENV}
+
+ # Restore the cashes that were prepared for all OS
+ - name: Restore from cache on Linux
+ id: restore-linux
+ uses: actions/cache/restore@v4
with:
path: dist/linux_amd64
key: linux-${{ env.sha_short }}
- - uses: actions/cache@v4
+ fail-on-cache-miss: true
+
+ - name: Save from cache on MacOS
+ id: restore-macos
+ uses: actions/cache/restore@v4
with:
path: dist/darwin_amd64
key: darwin-${{ env.sha_short }}
- - uses: actions/cache@v4
+ fail-on-cache-miss: true
+
+ - name: Restore from cache on Windows
+ id: restore-windows
+ uses: actions/cache/restore@v4
with:
path: dist/windows_amd64
key: windows-${{ env.sha_short }}
+ fail-on-cache-miss: true
enableCrossOsArchive: true
+ # Technically the following should never happen as we are using the `fail-on-cache-miss=true`
+ # so it would fail before reaching here, but leaving for now incase the option is removed.
+ - name: Exit if failed to restore cache for any OS
+ if: |
+ steps.restore-linux.outputs.cache-hit != 'true' ||
+ steps.restore-macos.outputs.cache-hit != 'true' ||
+ steps.restore-windows.outputs.cache-hit != 'true'
+ run: exit 1
- # release
- - uses: goreleaser/goreleaser-action@v5
- if: steps.cache.outputs.cache-hit != 'true' # do not run if cache hit
+ - name: Do the release, only if all OS caches were restored
+ uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser-pro
version: latest
@@ -149,7 +178,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
-
+
pull-docker-image:
name: Pull docker image job
runs-on: ubuntu-latest
@@ -168,9 +197,9 @@ jobs:
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
-
+
- name: Pull Docker image
run: docker pull ${{ matrix.image_tag }}
- name: Test Docker image
- run: docker run --rm ${{ matrix.image_tag }}
\ No newline at end of file
+ run: docker run --rm ${{ matrix.image_tag }}
diff --git a/.github/workflows/start-binary.yml b/.github/workflows/start-binary.yml
index 267466b8a3..35fea3c022 100644
--- a/.github/workflows/start-binary.yml
+++ b/.github/workflows/start-binary.yml
@@ -37,7 +37,7 @@ jobs:
- name: Setup Go environment explicitly
uses: actions/setup-go@v3
with:
- go-version: "1.20"
+ go-version: "1.21"
check-latest: true
- name: Build modules
diff --git a/.github/workflows/test-and-upload-coverage.yml b/.github/workflows/test-and-upload-coverage.yml
index de7c9df848..60858b1f86 100644
--- a/.github/workflows/test-and-upload-coverage.yml
+++ b/.github/workflows/test-and-upload-coverage.yml
@@ -75,7 +75,7 @@ jobs:
- name: Setup Go environment explicitly
uses: actions/setup-go@v3
with:
- go-version: "1.20"
+ go-version: "1.21"
check-latest: true
- name: Build dependencies
@@ -105,7 +105,7 @@ jobs:
needs: run-tests
- # Important to know:
+ # Important to know:
# - We didn't use `if: always()` here, so this job doesn't run if we manually canceled.
# - `if: success()` is always implied unless `always()` or `failure()` is specified.
if: success() || failure()
diff --git a/.github/workflows/validate-containerfile.yml b/.github/workflows/validate-containerfile.yml
index b3315861ad..260e0dba89 100644
--- a/.github/workflows/validate-containerfile.yml
+++ b/.github/workflows/validate-containerfile.yml
@@ -54,4 +54,3 @@ jobs:
- name: Test Docker image
run: docker run --rm ${{ env.TEST_TAG }}
-
diff --git a/.gitignore b/.gitignore
index 81c1a16d62..40eac1780c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,12 @@ tests/lenses/rust_wasm32_remove/pkg
tests/lenses/rust_wasm32_copy/Cargo.lock
tests/lenses/rust_wasm32_copy/target
tests/lenses/rust_wasm32_copy/pkg
+tests/lenses/rust_wasm32_prepend/Cargo.lock
+tests/lenses/rust_wasm32_prepend/target
+tests/lenses/rust_wasm32_prepend/pkg
+tests/lenses/rust_wasm32_filter/Cargo.lock
+tests/lenses/rust_wasm32_filter/target
+tests/lenses/rust_wasm32_filter/pkg
# Ignore OS X metadata files.
.history
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
index 119447d180..05f201200f 100644
--- a/.goreleaser.yaml
+++ b/.goreleaser.yaml
@@ -15,7 +15,7 @@ builds:
goarch:
- amd64
- arm64
- # A build with the playground included.
+ # A build with the playground included.
- id: "defradb_playground"
main: ./cmd/defradb
flags:
@@ -27,30 +27,44 @@ builds:
goarch:
- amd64
- arm64
-
+
partial:
by: target
archives:
- id: defradb_playground
- builds:
+ builds:
- defradb_playground
format: binary
# this name template makes the OS and Arch compatible with the results of `uname`.
- name_template: '{{ .Binary }}_playground_{{ .Version }}_{{ .Os }}_{{- if eq .Arch "amd64" }}x86_64{{- else }}{{ .Arch }}{{ end }}{{- if .Arm }}v{{ .Arm }}{{ end }}'
+ name_template: >-
+ {{ .Binary }}_playground_{{ .Version }}_{{ .Os }}_
+ {{- if eq .Arch "amd64" }}x86_64
+ {{- else }}{{ .Arch }}{{ end }}
+ {{- if .Arm }}v{{ .Arm }}{{ end }}
- id: defradb
- builds:
+ builds:
- defradb
format: binary
# this name template makes the OS and Arch compatible with the results of `uname`.
- name_template: '{{ .Binary }}_{{ .Version }}_{{ .Os }}_{{- if eq .Arch "amd64" }}x86_64{{- else }}{{ .Arch }}{{ end }}{{- if .Arm }}v{{ .Arm }}{{ end }}'
+ name_template: >-
+ {{ .Binary }}_{{ .Version }}_{{ .Os }}_
+ {{- if eq .Arch "amd64" }}x86_64
+ {{- else }}{{ .Arch }}{{ end }}
+ {{- if .Arm }}v{{ .Arm }}{{ end }}
release:
target_commitish: '{{ .Commit }}'
- header: |
- DefraDB v{{ .Major }}.{{ .Minor }} is a major pre-production release. Until the stable version 1.0 is reached, the SemVer minor patch number will denote notable releases, which will give the project freedom to experiment and explore potentially breaking changes.
+ header: >
+ DefraDB v{{ .Major }}.{{ .Minor }} is a major pre-production release.
+ Until the stable version 1.0 is reached, the SemVer minor patch number will denote notable releases,
+ which will give the project freedom to experiment and explore potentially breaking changes.
+
+ To get a full outline of the changes, we invite you to review the official changelog below.
+ This release does include a Breaking Change to existing v{{ .Major }}.{{ .Minor }}.x databases.
+ If you need help migrating an existing deployment, reach out at hello@source.network or join
+ our Discord at https://discord.gg/w7jYQVJ/.
- To get a full outline of the changes, we invite you to review the official changelog below. This release does include a Breaking Change to existing v{{ .Major }}.{{ .Minor }}.x databases. If you need help migrating an existing deployment, reach out at hello@source.network or join our Discord at https://discord.gg/w7jYQVJ/.
name_template: "v{{ .Version }} Release"
changelog:
@@ -85,21 +99,21 @@ milestones:
name_template: "DefraDB v{{ .Major }}.{{ .Minor }}"
dockers:
-- ids:
- - "defradb_playground"
- image_templates:
- - "{{ .Env.GITHUB_REPOSITORY }}:latest"
- - "{{ .Env.GITHUB_REPOSITORY }}:{{ .Version }}"
- - "ghcr.io/{{ .Env.GITHUB_REPOSITORY }}:{{ .Version }}"
- use: buildx
- build_flag_templates:
- - "--pull"
- - "--label=org.opencontainers.image.description=DefraDB is a Peer-to-Peer Edge Database."
- - "--label=org.opencontainers.image.created={{ .Date }}"
- - "--label=org.opencontainers.image.name={{ .ProjectName }}"
- - "--label=org.opencontainers.image.revision={{ .FullCommit }}"
- - "--label=org.opencontainers.image.version={{ .Version }}"
- - "--label=org.opencontainers.image.source={{ .GitURL }}"
- - "--platform=linux/amd64"
- dockerfile: ./tools/goreleaser.containerfile
-
+ - ids:
+ - "defradb_playground"
+ image_templates:
+ - "{{ .Env.GITHUB_REPOSITORY }}:latest"
+ - "{{ .Env.GITHUB_REPOSITORY }}:{{ .Version }}"
+ - "ghcr.io/{{ .Env.GITHUB_REPOSITORY }}:latest"
+ - "ghcr.io/{{ .Env.GITHUB_REPOSITORY }}:{{ .Version }}"
+ use: buildx
+ build_flag_templates:
+ - "--pull"
+ - "--label=org.opencontainers.image.description=DefraDB is a Peer-to-Peer Edge Database."
+ - "--label=org.opencontainers.image.created={{ .Date }}"
+ - "--label=org.opencontainers.image.name={{ .ProjectName }}"
+ - "--label=org.opencontainers.image.revision={{ .FullCommit }}"
+ - "--label=org.opencontainers.image.version={{ .Version }}"
+ - "--label=org.opencontainers.image.source={{ .GitURL }}"
+ - "--platform=linux/amd64"
+ dockerfile: ./tools/goreleaser.containerfile
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6fa2ed67e2..342cfb3a53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,71 @@
+
+## [v0.10.0](https://github.com/sourcenetwork/defradb/compare/v0.9.0...v0.10.0)
+
+> 2024-03-08
+
+DefraDB v0.10 is a major pre-production release. Until the stable version 1.0 is reached, the SemVer minor patch number will denote notable releases, which will give the project freedom to experiment and explore potentially breaking changes.
+
+To get a full outline of the changes, we invite you to review the official changelog below. This release does include a Breaking Change to existing v0.9.x databases. If you need help migrating an existing deployment, reach out at [hello@source.network](mailto:hello@source.network) or join our Discord at https://discord.gg/w7jYQVJ/.
+
+### Features
+
+* Add case insensitive `like` operator ([#2368](https://github.com/sourcenetwork/defradb/issues/2368))
+* Reverted order for indexed fields ([#2335](https://github.com/sourcenetwork/defradb/issues/2335))
+* Rework GetCollection/SchemaByFoo funcs into single ([#2319](https://github.com/sourcenetwork/defradb/issues/2319))
+* Add support for views with Lens transforms ([#2311](https://github.com/sourcenetwork/defradb/issues/2311))
+* Model Col. SchemaVersions and migrations on Cols ([#2286](https://github.com/sourcenetwork/defradb/issues/2286))
+* Replace FieldDescription.RelationType with IsPrimary ([#2288](https://github.com/sourcenetwork/defradb/issues/2288))
+* Multiple docs with nil value on unique-indexed field ([#2276](https://github.com/sourcenetwork/defradb/issues/2276))
+* Allow setting null values on doc fields ([#2273](https://github.com/sourcenetwork/defradb/issues/2273))
+* Add JSON scalar ([#2254](https://github.com/sourcenetwork/defradb/issues/2254))
+* Generate OpenAPI command ([#2235](https://github.com/sourcenetwork/defradb/issues/2235))
+* Add composite indexes ([#2226](https://github.com/sourcenetwork/defradb/issues/2226))
+
+### Fixes
+
+* Add `latest` image tag for ghcr ([#2340](https://github.com/sourcenetwork/defradb/issues/2340))
+* Move field id off of schema ([#2336](https://github.com/sourcenetwork/defradb/issues/2336))
+* Make returned collections respect explicit transactions ([#2385](https://github.com/sourcenetwork/defradb/issues/2385))
+* Update GetCollections behaviour ([#2378](https://github.com/sourcenetwork/defradb/issues/2378))
+* Add missing directive definitions ([#2369](https://github.com/sourcenetwork/defradb/issues/2369))
+* Add validation to JSON fields ([#2375](https://github.com/sourcenetwork/defradb/issues/2375))
+* Make peers sync secondary index ([#2390](https://github.com/sourcenetwork/defradb/issues/2390))
+* Load root dir before loading config ([#2266](https://github.com/sourcenetwork/defradb/issues/2266))
+* Mark docs as deleted when querying in delete mut ([#2298](https://github.com/sourcenetwork/defradb/issues/2298))
+* Add missing logs at startup ([#2391](https://github.com/sourcenetwork/defradb/issues/2391))
+* Add missing delta payload ([#2306](https://github.com/sourcenetwork/defradb/issues/2306))
+* Fix compound relational filters in aggregates ([#2297](https://github.com/sourcenetwork/defradb/issues/2297))
+
+### Refactoring
+
+* Generate field ids using a sequence ([#2339](https://github.com/sourcenetwork/defradb/issues/2339))
+* Make config internal to CLI ([#2310](https://github.com/sourcenetwork/defradb/issues/2310))
+* Node config ([#2296](https://github.com/sourcenetwork/defradb/issues/2296))
+* HTTP config ([#2278](https://github.com/sourcenetwork/defradb/issues/2278))
+* Remove unused Delete field from client.Document ([#2275](https://github.com/sourcenetwork/defradb/issues/2275))
+* Decouple net config ([#2258](https://github.com/sourcenetwork/defradb/issues/2258))
+* Make CollectionDescription.Name Option ([#2223](https://github.com/sourcenetwork/defradb/issues/2223))
+
+### Chore
+
+* Bump to GoLang v1.21 ([#2195](https://github.com/sourcenetwork/defradb/issues/2195))
+
+### Bot
+
+* Update dependencies (bulk dependabot PRs) 05-02-2024 ([#2372](https://github.com/sourcenetwork/defradb/issues/2372))
+* Update dependencies (bulk dependabot PRs) 02-27-2024 ([#2353](https://github.com/sourcenetwork/defradb/issues/2353))
+* Bump [@typescript](https://github.com/typescript)-eslint/eslint-plugin from 6.21.0 to 7.0.1 in /playground ([#2331](https://github.com/sourcenetwork/defradb/issues/2331))
+* Bump google.golang.org/grpc from 1.61.0 to 1.61.1 ([#2320](https://github.com/sourcenetwork/defradb/issues/2320))
+* Update dependencies (bulk dependabot PRs) 2024-02-19 ([#2330](https://github.com/sourcenetwork/defradb/issues/2330))
+* Bump vite from 5.1.1 to 5.1.2 in /playground ([#2317](https://github.com/sourcenetwork/defradb/issues/2317))
+* Bump golang.org/x/net from 0.20.0 to 0.21.0 ([#2301](https://github.com/sourcenetwork/defradb/issues/2301))
+* Update dependencies (bulk dependabot PRs) 2023-02-14 ([#2313](https://github.com/sourcenetwork/defradb/issues/2313))
+* Update dependencies (bulk dependabot PRs) 02-07-2024 ([#2294](https://github.com/sourcenetwork/defradb/issues/2294))
+* Update dependencies (bulk dependabot PRs) 30-01-2024 ([#2270](https://github.com/sourcenetwork/defradb/issues/2270))
+* Update dependencies (bulk dependabot PRs) 23-01-2024 ([#2252](https://github.com/sourcenetwork/defradb/issues/2252))
+* Bump vite from 5.0.11 to 5.0.12 in /playground ([#2236](https://github.com/sourcenetwork/defradb/issues/2236))
+* Bump github.com/evanphx/json-patch/v5 from 5.7.0 to 5.8.1 ([#2233](https://github.com/sourcenetwork/defradb/issues/2233))
+
## [v0.9.0](https://github.com/sourcenetwork/defradb/compare/v0.8.0...v0.9.0)
diff --git a/Makefile b/Makefile
index 0ddde9790f..cde535be4b 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,33 @@ ifndef VERBOSE
MAKEFLAGS+=--no-print-directory
endif
+# Detect OS (`Linux`, `Darwin`, `Windows`)
+# Note: can use `lsb_release --id --short` for more specfic linux distro information.
+OS_GENERAL := Unknown
+ifeq ($(OS),Windows_NT)
+ OS_GENERAL := Windows
+else
+ OS_GENERAL := $(shell sh -c 'uname 2>/dev/null || echo Unknown')
+endif
+
+# Detect OS specfic package manager if possible (`apt`, `yum`, `pacman`, `brew`, `choco`)
+OS_PACKAGE_MANAGER := Unknown
+ifeq ($(OS_GENERAL),Linux)
+ ifneq ($(shell which apt 2>/dev/null),)
+ OS_PACKAGE_MANAGER := apt
+ else ifneq ($(shell which yum 2>/dev/null),)
+ OS_PACKAGE_MANAGER := yum
+ else ifneq ($(shell which pacman 2>/dev/null),)
+ OS_PACKAGE_MANAGER := pacman
+ else ifneq ($(shell which dnf 2>/dev/null),)
+ OS_PACKAGE_MANAGER := dnf
+ endif
+else ifeq ($(OS_GENERAL),Darwin)
+ OS_PACKAGE_MANAGER := brew
+else ifeq ($(OS_GENERAL),Windows)
+ OS_PACKAGE_MANAGER := choco
+endif
+
# Provide info from git to the version package using linker flags.
ifeq (, $(shell which git))
$(error "No git in $(PATH), version information won't be included")
@@ -18,6 +45,15 @@ else
VERSION_GITRELEASE=$(shell git describe --tags)
endif
+$(info ----------------------------------------);
+$(info OS = $(OS_GENERAL));
+$(info PACKAGE_MANAGER = $(OS_PACKAGE_MANAGER));
+$(info GOINFO = $(VERSION_GOINFO));
+$(info GITCOMMIT = $(VERSION_GITCOMMIT));
+$(info GITCOMMITDATE = $(VERSION_GITCOMMITDATE));
+$(info GITRELEASE = $(VERSION_GITRELEASE));
+$(info ----------------------------------------);
+
BUILD_FLAGS=-trimpath -ldflags "\
-X 'github.com/sourcenetwork/defradb/version.GoInfo=$(VERSION_GOINFO)'\
-X 'github.com/sourcenetwork/defradb/version.GitRelease=$(VERSION_GITRELEASE)'\
@@ -36,9 +72,8 @@ COVERAGE_FILE=coverage.txt
COVERAGE_FLAGS=-covermode=atomic -coverpkg=./... -args -test.gocoverdir=$(COVERAGE_DIRECTORY)
PLAYGROUND_DIRECTORY=playground
-LENS_TEST_DIRECTORY=tests/integration/schema/migrations
CHANGE_DETECTOR_TEST_DIRECTORY=tests/change_detector
-DEFAULT_TEST_DIRECTORIES=$$(go list ./... | grep -v -e $(LENS_TEST_DIRECTORY))
+DEFAULT_TEST_DIRECTORIES=./...
default:
@go run $(BUILD_FLAGS) cmd/defradb/main.go
@@ -47,6 +82,15 @@ default:
install:
@go install $(BUILD_FLAGS) ./cmd/defradb
+.PHONY: install\:manpages
+install\:manpages:
+ifeq ($(OS_GENERAL),Linux)
+ cp build/man/* /usr/share/man/man1/
+endif
+ifneq ($(OS_GENERAL),Linux)
+ @echo "Direct installation of Defradb's man pages is not supported on your system."
+endif
+
# Usage:
# - make build
# - make build path="path/to/defradb-binary"
@@ -78,16 +122,27 @@ client\:dump:
client\:add-schema:
./build/defradb client schema add -f examples/schema/bookauthpub.graphql
+.PHONY: deps\:lint-go
+deps\:lint-go:
+ go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54
+
+.PHONY: deps\:lint-yaml
+deps\:lint-yaml:
+ifeq (, $(shell which yamllint))
+ $(info YAML linter 'yamllint' not found on the system, please install it.)
+ $(info Can try using your local package manager: $(OS_PACKAGE_MANAGER))
+else
+ $(info YAML linter 'yamllint' already installed.)
+endif
+
.PHONY: deps\:lint
deps\:lint:
- go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.54
+ @$(MAKE) deps:lint-go && \
+ $(MAKE) deps:lint-yaml
.PHONY: deps\:test
deps\:test:
go install gotest.tools/gotestsum@latest
-
-.PHONY: deps\:lens
-deps\:lens:
rustup target add wasm32-unknown-unknown
@$(MAKE) -C ./tests/lenses build
@@ -147,7 +202,7 @@ verify:
.PHONY: tidy
tidy:
- go mod tidy -go=1.20
+ go mod tidy -go=1.21
.PHONY: clean
clean:
@@ -217,26 +272,6 @@ test\:cli:
test\:names:
gotestsum --format testname -- $(DEFAULT_TEST_DIRECTORIES) $(TEST_FLAGS)
-.PHONY: test\:lens
-test\:lens:
- @$(MAKE) deps:lens
- gotestsum --format testname -- ./$(LENS_TEST_DIRECTORY)/... $(TEST_FLAGS)
-
-.PHONY: test\:lens-quick
-test\:lens-quick:
- @$(MAKE) deps:lens
- gotestsum --format testname -- ./$(LENS_TEST_DIRECTORY)/...
-
-.PHONY: test\:all
-test\:all:
- @$(MAKE) test:names
- @$(MAKE) test:lens
-
-.PHONY: test\:all-quick
-test\:all-quick:
- @$(MAKE) test:quick
- @$(MAKE) test:lens-quick
-
.PHONY: test\:verbose
test\:verbose:
gotestsum --format standard-verbose -- $(DEFAULT_TEST_DIRECTORIES) $(TEST_FLAGS)
@@ -263,7 +298,6 @@ test\:scripts:
.PHONY: test\:coverage
test\:coverage:
- @$(MAKE) deps:lens
@$(MAKE) clean:coverage
mkdir $(COVERAGE_DIRECTORY)
ifeq ($(path),)
@@ -300,6 +334,7 @@ validate\:circleci:
.PHONY: lint
lint:
golangci-lint run --config tools/configs/golangci.yaml
+ yamllint -c tools/configs/yamllint.yaml .
.PHONY: lint\:fix
lint\:fix:
@@ -334,13 +369,3 @@ docs\:manpages:
docs\:godoc:
godoc -http=:6060
# open http://localhost:6060/pkg/github.com/sourcenetwork/defradb/
-
-detectedOS := $(shell uname)
-.PHONY: install\:manpages
-install\:manpages:
-ifeq ($(detectedOS),Linux)
- cp build/man/* /usr/share/man/man1/
-endif
-ifneq ($(detectedOS),Linux)
- @echo "Direct installation of Defradb's man pages is not supported on your system."
-endif
diff --git a/README.md b/README.md
index 0e5997b902..a7156888b9 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@ Read the documentation on [docs.source.network](https://docs.source.network/).
- [Install](#install)
- [Start](#start)
- [Configuration](#configuration)
+- [External port binding](#external-port-binding)
- [Add a schema type](#add-a-schema-type)
- [Create a document instance](#create-a-document-instance)
- [Query documents](#query-documents)
@@ -72,6 +73,16 @@ In this document, we use the default configuration, which has the following beha
The GraphQL endpoint can be used with a GraphQL client (e.g., Altair) to conveniently perform requests (`query`, `mutation`) and obtain schema introspection.
+Read more about the configuration [here](./docs/config.md).
+
+## External port binding
+
+By default the HTTP API and P2P network will use localhost. If you want to expose the ports externally you need to specify the addresses in the config or command line parameters.
+
+```
+defradb start --p2paddr /ip4/0.0.0.0/tcp/9171 --url 0.0.0.0:9181
+```
+
## Add a schema type
Schemas are used to structure documents using a type system.
@@ -235,7 +246,6 @@ DQL is compatible with GraphQL but features various extensions.
Read its documentation at [docs.source.network](https://docs.source.network/references/query-specification/query-language-overview) to discover its filtering, ordering, limiting, relationships, variables, aggregate functions, and other useful features.
-
## Peer-to-peer data synchronization
DefraDB leverages peer-to-peer networking for data exchange, synchronization, and replication of documents and commits.
@@ -280,14 +290,14 @@ In this example, we use `12D3KooWNXm3dmrwCYSxGoRUyZstaKYiHPdt8uZH5vgVaEJyzU8B`,
For *nodeB*, we provide the following configuration:
```shell
-defradb start --rootdir ~/.defradb-nodeB --url localhost:9182 --p2paddr /ip4/0.0.0.0/tcp/9172 --peers /ip4/0.0.0.0/tcp/9171/p2p/12D3KooWNXm3dmrwCYSxGoRUyZstaKYiHPdt8uZH5vgVaEJyzU8B
+defradb start --rootdir ~/.defradb-nodeB --url localhost:9182 --p2paddr /ip4/127.0.0.1/tcp/9172 --peers /ip4/127.0.0.1/tcp/9171/p2p/12D3KooWNXm3dmrwCYSxGoRUyZstaKYiHPdt8uZH5vgVaEJyzU8B
```
About the flags:
- `--rootdir` specifies the root dir (config and data) to use
- `--url` is the address to listen on for the client HTTP and GraphQL API
-- `--p2paddr` is the multiaddress for the P2P networking to listen on
+- `--p2paddr` is a comma-separated list of multiaddresses to listen on for p2p networking
- `--peers` is a comma-separated list of peer multiaddresses
This starts two nodes and connects them via pubsub networking.
@@ -387,16 +397,6 @@ defradb start --tls --pubkeypath ~/path-to-pubkey.key --privkeypath ~/path-to-pr
```
-DefraDB also comes with automatic HTTPS for deployments on the public web. To enable HTTPS,
- deploy DefraDB to a server with both port 80 and port 443 open. With your domain's DNS A record
- pointed to the IP of your server, you can run the database using the following command:
-```shell
-sudo defradb start --tls --url=your-domain.net --email=email@example.com
-```
-Note: `sudo` is needed above for the redirection server (to bind port 80).
-
-A valid email address is necessary for the creation of the certificate, and is important to get notifications from the Certificate Authority - in case the certificate is about to expire, etc.
-
## Supporting CORS
When accessing DefraDB through a frontend interface, you may be confronted with a CORS error. That is because, by default, DefraDB will not have any allowed origins set. To specify which origins should be allowed to access your DefraDB endpoint, you can specify them when starting the database:
diff --git a/cli/backup_export.go b/cli/backup_export.go
index 9e8d1c056e..b905bdf9c7 100644
--- a/cli/backup_export.go
+++ b/cli/backup_export.go
@@ -38,7 +38,7 @@ Example: export data for the 'Users' collection:
defradb client export --collection Users user_data.json`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
- store := mustGetStoreContext(cmd)
+ store := mustGetContextStore(cmd)
if !isValidExportFormat(format) {
return ErrInvalidExportFormat
diff --git a/cli/backup_import.go b/cli/backup_import.go
index 35af345a0a..56f1907643 100644
--- a/cli/backup_import.go
+++ b/cli/backup_import.go
@@ -24,7 +24,7 @@ Example: import data to the database:
defradb client import user_data.json`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
- store := mustGetStoreContext(cmd)
+ store := mustGetContextStore(cmd)
return store.BasicImport(cmd.Context(), args[0])
},
}
diff --git a/cli/cli.go b/cli/cli.go
index 2ee882afce..4cdb8c443b 100644
--- a/cli/cli.go
+++ b/cli/cli.go
@@ -16,14 +16,13 @@ package cli
import (
"github.com/spf13/cobra"
- "github.com/sourcenetwork/defradb/config"
"github.com/sourcenetwork/defradb/logging"
)
var log = logging.MustNewLogger("cli")
// NewDefraCommand returns the root command instanciated with its tree of subcommands.
-func NewDefraCommand(cfg *config.Config) *cobra.Command {
+func NewDefraCommand() *cobra.Command {
p2p_collection := MakeP2PCollectionCommand()
p2p_collection.AddCommand(
MakeP2PCollectionAddCommand(),
@@ -48,7 +47,7 @@ func NewDefraCommand(cfg *config.Config) *cobra.Command {
schema_migrate := MakeSchemaMigrationCommand()
schema_migrate.AddCommand(
MakeSchemaMigrationSetCommand(),
- MakeSchemaMigrationGetCommand(),
+ MakeSchemaMigrationSetRegistryCommand(),
MakeSchemaMigrationReloadCommand(),
MakeSchemaMigrationUpCommand(),
MakeSchemaMigrationDownCommand(),
@@ -58,7 +57,7 @@ func NewDefraCommand(cfg *config.Config) *cobra.Command {
schema.AddCommand(
MakeSchemaAddCommand(),
MakeSchemaPatchCommand(),
- MakeSchemaSetDefaultCommand(),
+ MakeSchemaSetActiveCommand(),
MakeSchemaDescribeCommand(),
schema_migrate,
)
@@ -83,12 +82,12 @@ func NewDefraCommand(cfg *config.Config) *cobra.Command {
tx := MakeTxCommand()
tx.AddCommand(
- MakeTxCreateCommand(cfg),
- MakeTxCommitCommand(cfg),
- MakeTxDiscardCommand(cfg),
+ MakeTxCreateCommand(),
+ MakeTxCommitCommand(),
+ MakeTxDiscardCommand(),
)
- collection := MakeCollectionCommand(cfg)
+ collection := MakeCollectionCommand()
collection.AddCommand(
MakeCollectionGetCommand(),
MakeCollectionListDocIDsCommand(),
@@ -98,7 +97,7 @@ func NewDefraCommand(cfg *config.Config) *cobra.Command {
MakeCollectionDescribeCommand(),
)
- client := MakeClientCommand(cfg)
+ client := MakeClientCommand()
client.AddCommand(
MakeDumpCommand(),
MakeRequestCommand(),
@@ -111,13 +110,12 @@ func NewDefraCommand(cfg *config.Config) *cobra.Command {
collection,
)
- root := MakeRootCommand(cfg)
+ root := MakeRootCommand()
root.AddCommand(
client,
- MakeStartCommand(cfg),
- MakeServerDumpCmd(cfg),
+ MakeStartCommand(),
+ MakeServerDumpCmd(),
MakeVersionCommand(),
- MakeInitCommand(cfg),
)
return root
diff --git a/cli/client.go b/cli/client.go
index 8866294f69..532712e8f8 100644
--- a/cli/client.go
+++ b/cli/client.go
@@ -12,11 +12,9 @@ package cli
import (
"github.com/spf13/cobra"
-
- "github.com/sourcenetwork/defradb/config"
)
-func MakeClientCommand(cfg *config.Config) *cobra.Command {
+func MakeClientCommand() *cobra.Command {
var txID uint64
var cmd = &cobra.Command{
Use: "client",
@@ -24,13 +22,16 @@ func MakeClientCommand(cfg *config.Config) *cobra.Command {
Long: `Interact with a DefraDB node.
Execute queries, add schema types, obtain node info, etc.`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
- if err := loadConfig(cfg); err != nil {
+ if err := setContextRootDir(cmd); err != nil {
+ return err
+ }
+ if err := setContextConfig(cmd); err != nil {
return err
}
- if err := setTransactionContext(cmd, cfg, txID); err != nil {
+ if err := setContextTransaction(cmd, txID); err != nil {
return err
}
- return setStoreContext(cmd, cfg)
+ return setContextStore(cmd)
},
}
cmd.PersistentFlags().Uint64Var(&txID, "tx", 0, "Transaction ID")
diff --git a/cli/collection.go b/cli/collection.go
index 8af1839b47..23ef9194ae 100644
--- a/cli/collection.go
+++ b/cli/collection.go
@@ -13,86 +13,63 @@ package cli
import (
"context"
+ "github.com/sourcenetwork/immutable"
"github.com/spf13/cobra"
"github.com/sourcenetwork/defradb/client"
- "github.com/sourcenetwork/defradb/config"
"github.com/sourcenetwork/defradb/datastore"
)
-func MakeCollectionCommand(cfg *config.Config) *cobra.Command {
+func MakeCollectionCommand() *cobra.Command {
var txID uint64
var name string
var schemaRoot string
var versionID string
+ var getInactive bool
var cmd = &cobra.Command{
Use: "collection [--name --schema --version ]",
Short: "Interact with a collection.",
Long: `Create, read, update, and delete documents within a collection.`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) (err error) {
// cobra does not chain pre run calls so we have to run them again here
- if err := loadConfig(cfg); err != nil {
+ if err := setContextRootDir(cmd); err != nil {
return err
}
- if err := setTransactionContext(cmd, cfg, txID); err != nil {
+ if err := setContextConfig(cmd); err != nil {
return err
}
- if err := setStoreContext(cmd, cfg); err != nil {
+ if err := setContextTransaction(cmd, txID); err != nil {
return err
}
- store := mustGetStoreContext(cmd)
-
- var col client.Collection
- var cols []client.Collection
- switch {
- case versionID != "":
- cols, err = store.GetCollectionsByVersionID(cmd.Context(), versionID)
-
- case schemaRoot != "":
- cols, err = store.GetCollectionsBySchemaRoot(cmd.Context(), schemaRoot)
-
- case name != "":
- col, err = store.GetCollectionByName(cmd.Context(), name)
- cols = []client.Collection{col}
-
- default:
- return nil
- }
-
- if err != nil {
+ if err := setContextStore(cmd); err != nil {
return err
}
+ store := mustGetContextStore(cmd)
- if schemaRoot != "" && versionID != "" && len(cols) > 0 {
- if cols[0].SchemaRoot() != schemaRoot {
- // If the a versionID has been provided that does not pair up with the given schema root
- // we should error and let the user know they have provided impossible params.
- // We only need to check the first item - they will all be the same.
- return NewErrSchemaVersionNotOfSchema(schemaRoot, versionID)
- }
+ options := client.CollectionFetchOptions{}
+ if versionID != "" {
+ options.SchemaVersionID = immutable.Some(versionID)
+ }
+ if schemaRoot != "" {
+ options.SchemaRoot = immutable.Some(schemaRoot)
}
-
if name != "" {
- // Multiple params may have been specified, and in some cases both are needed.
- // For example if a schema version and a collection name have been provided,
- // we need to ensure that a collection at the requested version is returned.
- // Likewise we need to ensure that if a collection name and schema id are provided,
- // but there are none matching both, that nothing is returned.
- fetchedCols := cols
- cols = nil
- for _, c := range fetchedCols {
- if c.Name() == name {
- cols = append(cols, c)
- break
- }
- }
+ options.Name = immutable.Some(name)
+ }
+ if getInactive {
+ options.IncludeInactive = immutable.Some(getInactive)
+ }
+
+ cols, err := store.GetCollections(cmd.Context(), options)
+ if err != nil {
+ return err
}
if len(cols) != 1 {
// If more than one collection matches the given criteria we cannot set the context collection
return nil
}
- col = cols[0]
+ col := cols[0]
if tx, ok := cmd.Context().Value(txContextKey).(datastore.Txn); ok {
col = col.WithTxn(tx)
@@ -107,5 +84,6 @@ func MakeCollectionCommand(cfg *config.Config) *cobra.Command {
cmd.PersistentFlags().StringVar(&name, "name", "", "Collection name")
cmd.PersistentFlags().StringVar(&schemaRoot, "schema", "", "Collection schema Root")
cmd.PersistentFlags().StringVar(&versionID, "version", "", "Collection version ID")
+ cmd.PersistentFlags().BoolVar(&getInactive, "get-inactive", false, "Get inactive collections as well as active")
return cmd
}
diff --git a/cli/collection_create.go b/cli/collection_create.go
index 82e1e5db09..efeee61494 100644
--- a/cli/collection_create.go
+++ b/cli/collection_create.go
@@ -40,7 +40,7 @@ Example: create from stdin
`,
Args: cobra.RangeArgs(0, 1),
RunE: func(cmd *cobra.Command, args []string) error {
- col, ok := tryGetCollectionContext(cmd)
+ col, ok := tryGetContextCollection(cmd)
if !ok {
return cmd.Usage()
}
diff --git a/cli/collection_delete.go b/cli/collection_delete.go
index dcd7c9d872..d1f945d9ae 100644
--- a/cli/collection_delete.go
+++ b/cli/collection_delete.go
@@ -31,7 +31,7 @@ Example: delete by filter
defradb client collection delete --name User --filter '{ "_gte": { "points": 100 } }'
`,
RunE: func(cmd *cobra.Command, args []string) error {
- col, ok := tryGetCollectionContext(cmd)
+ col, ok := tryGetContextCollection(cmd)
if !ok {
return cmd.Usage()
}
diff --git a/cli/collection_describe.go b/cli/collection_describe.go
index a21c4d0c10..5d1a85ea5e 100644
--- a/cli/collection_describe.go
+++ b/cli/collection_describe.go
@@ -11,12 +11,17 @@
package cli
import (
+ "github.com/sourcenetwork/immutable"
"github.com/spf13/cobra"
"github.com/sourcenetwork/defradb/client"
)
func MakeCollectionDescribeCommand() *cobra.Command {
+ var name string
+ var schemaRoot string
+ var versionID string
+ var getInactive bool
var cmd = &cobra.Command{
Use: "describe",
Short: "View collection description.",
@@ -28,21 +33,33 @@ Example: view all collections
Example: view collection by name
defradb client collection describe --name User
-Example: view collection by schema id
+Example: view collection by schema root id
defradb client collection describe --schema bae123
-Example: view collection by version id
+Example: view collection by version id. This will also return inactive collections
defradb client collection describe --version bae123
`,
RunE: func(cmd *cobra.Command, args []string) error {
- store := mustGetStoreContext(cmd)
+ store := mustGetContextStore(cmd)
- col, ok := tryGetCollectionContext(cmd)
- if ok {
- return writeJSON(cmd, col.Definition())
+ options := client.CollectionFetchOptions{}
+ if versionID != "" {
+ options.SchemaVersionID = immutable.Some(versionID)
}
- // if no collection specified list all collections
- cols, err := store.GetAllCollections(cmd.Context())
+ if schemaRoot != "" {
+ options.SchemaRoot = immutable.Some(schemaRoot)
+ }
+ if name != "" {
+ options.Name = immutable.Some(name)
+ }
+ if getInactive {
+ options.IncludeInactive = immutable.Some(getInactive)
+ }
+
+ cols, err := store.GetCollections(
+ cmd.Context(),
+ options,
+ )
if err != nil {
return err
}
@@ -53,5 +70,9 @@ Example: view collection by version id
return writeJSON(cmd, colDesc)
},
}
+ cmd.Flags().StringVar(&name, "name", "", "Collection name")
+ cmd.Flags().StringVar(&schemaRoot, "schema", "", "Collection schema Root")
+ cmd.Flags().StringVar(&versionID, "version", "", "Collection version ID")
+ cmd.Flags().BoolVar(&getInactive, "get-inactive", false, "Get inactive collections as well as active")
return cmd
}
diff --git a/cli/collection_get.go b/cli/collection_get.go
index d753e0a8db..55c84d6289 100644
--- a/cli/collection_get.go
+++ b/cli/collection_get.go
@@ -28,7 +28,7 @@ Example:
`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
- col, ok := tryGetCollectionContext(cmd)
+ col, ok := tryGetContextCollection(cmd)
if !ok {
return cmd.Usage()
}
diff --git a/cli/collection_list_doc_ids.go b/cli/collection_list_doc_ids.go
index d7009cb300..7112a88817 100644
--- a/cli/collection_list_doc_ids.go
+++ b/cli/collection_list_doc_ids.go
@@ -26,7 +26,7 @@ Example:
defradb client collection docIDs --name User
`,
RunE: func(cmd *cobra.Command, args []string) error {
- col, ok := tryGetCollectionContext(cmd)
+ col, ok := tryGetContextCollection(cmd)
if !ok {
return cmd.Usage()
}
diff --git a/cli/collection_update.go b/cli/collection_update.go
index 9fd2deed3f..42354948a9 100644
--- a/cli/collection_update.go
+++ b/cli/collection_update.go
@@ -38,7 +38,7 @@ Example: update by docIDs
`,
Args: cobra.RangeArgs(0, 1),
RunE: func(cmd *cobra.Command, args []string) error {
- col, ok := tryGetCollectionContext(cmd)
+ col, ok := tryGetContextCollection(cmd)
if !ok {
return cmd.Usage()
}
diff --git a/cli/config.go b/cli/config.go
new file mode 100644
index 0000000000..bb57a8cb3d
--- /dev/null
+++ b/cli/config.go
@@ -0,0 +1,185 @@
+// Copyright 2024 Democratized Data Foundation
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+package cli
+
+import (
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/spf13/pflag"
+ "github.com/spf13/viper"
+
+ "github.com/sourcenetwork/defradb/logging"
+)
+
+const (
+ configStoreBadger = "badger"
+ configStoreMemory = "memory"
+ configLogFormatJSON = "json"
+ configLogFormatCSV = "csv"
+ configLogLevelInfo = "info"
+ configLogLevelDebug = "debug"
+ configLogLevelError = "error"
+ configLogLevelFatal = "fatal"
+)
+
+// configPaths are config keys that will be made relative to the rootdir
+var configPaths = []string{
+ "datastore.badger.path",
+ "api.pubkeypath",
+ "api.privkeypath",
+}
+
+// configFlags is a mapping of config keys to cli flags to bind to.
+var configFlags = map[string]string{
+ "log.level": "loglevel",
+ "log.output": "logoutput",
+ "log.format": "logformat",
+ "log.stacktrace": "logtrace",
+ "log.nocolor": "lognocolor",
+ "api.address": "url",
+ "datastore.maxtxnretries": "max-txn-retries",
+ "datastore.store": "store",
+ "datastore.badger.valuelogfilesize": "valuelogfilesize",
+ "net.peers": "peers",
+ "net.p2paddresses": "p2paddr",
+ "net.p2pdisabled": "no-p2p",
+ "api.allowed-origins": "allowed-origins",
+ "api.pubkeypath": "pubkeypath",
+ "api.privkeypath": "privkeypath",
+}
+
+// defaultConfig returns a new config with default values.
+func defaultConfig() *viper.Viper {
+ cfg := viper.New()
+
+ cfg.AutomaticEnv()
+ cfg.SetEnvPrefix("DEFRA")
+ cfg.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
+
+ cfg.SetConfigName("config")
+ cfg.SetConfigType("yaml")
+
+ cfg.SetDefault("datastore.badger.path", "data")
+ cfg.SetDefault("net.pubSubEnabled", true)
+ cfg.SetDefault("net.relay", false)
+ cfg.SetDefault("log.caller", false)
+
+ return cfg
+}
+
+// createConfig writes the default config file if one does not exist.
+func createConfig(rootdir string, flags *pflag.FlagSet) error {
+ cfg := defaultConfig()
+ cfg.AddConfigPath(rootdir)
+
+ if err := bindConfigFlags(cfg, flags); err != nil {
+ return err
+ }
+ // make sure rootdir exists
+ if err := os.MkdirAll(rootdir, 0755); err != nil {
+ return err
+ }
+ err := cfg.SafeWriteConfig()
+ // error type is known and shouldn't be wrapped
+ //
+ //nolint:errorlint
+ if _, ok := err.(viper.ConfigFileAlreadyExistsError); ok {
+ return nil
+ }
+ return err
+}
+
+// loadConfig returns a new config with values from the config in the given rootdir.
+func loadConfig(rootdir string, flags *pflag.FlagSet) (*viper.Viper, error) {
+ cfg := defaultConfig()
+ cfg.AddConfigPath(rootdir)
+
+ // attempt to read the existing config
+ err := cfg.ReadInConfig()
+ // error type is known and shouldn't be wrapped
+ //
+ //nolint:errorlint
+ if _, ok := err.(viper.ConfigFileNotFoundError); err != nil && !ok {
+ return nil, err
+ }
+ // bind cli flags to config keys
+ if err := bindConfigFlags(cfg, flags); err != nil {
+ return nil, err
+ }
+
+ // make paths relative to the rootdir
+ for _, key := range configPaths {
+ path := cfg.GetString(key)
+ if path != "" && !filepath.IsAbs(path) {
+ cfg.Set(key, filepath.Join(rootdir, path))
+ }
+ }
+
+ logCfg := loggingConfig(cfg.Sub("log"))
+ logCfg.OverridesByLoggerName = make(map[string]logging.Config)
+
+ // apply named logging overrides
+ for key := range cfg.GetStringMap("log.overrides") {
+ logCfg.OverridesByLoggerName[key] = loggingConfig(cfg.Sub("log.overrides." + key))
+ }
+ logging.SetConfig(logCfg)
+
+ return cfg, nil
+}
+
+// bindConfigFlags binds the set of cli flags to config values.
+func bindConfigFlags(cfg *viper.Viper, flags *pflag.FlagSet) error {
+ for key, flag := range configFlags {
+ err := cfg.BindPFlag(key, flags.Lookup(flag))
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// loggingConfig returns a new logging config from the given config.
+func loggingConfig(cfg *viper.Viper) logging.Config {
+ var level int8
+ switch value := cfg.GetString("level"); value {
+ case configLogLevelDebug:
+ level = logging.Debug
+ case configLogLevelInfo:
+ level = logging.Info
+ case configLogLevelError:
+ level = logging.Error
+ case configLogLevelFatal:
+ level = logging.Fatal
+ default:
+ level = logging.Info
+ }
+
+ var format logging.EncoderFormat
+ switch value := cfg.GetString("format"); value {
+ case configLogFormatJSON:
+ format = logging.JSON
+ case configLogFormatCSV:
+ format = logging.CSV
+ default:
+ format = logging.CSV
+ }
+
+ return logging.Config{
+ Level: logging.NewLogLevelOption(level),
+ EnableStackTrace: logging.NewEnableStackTraceOption(cfg.GetBool("stacktrace")),
+ DisableColor: logging.NewDisableColorOption(cfg.GetBool("nocolor")),
+ EncoderFormat: logging.NewEncoderFormatOption(format),
+ OutputPaths: []string{cfg.GetString("output")},
+ EnableCaller: logging.NewEnableCallerOption(cfg.GetBool("caller")),
+ }
+}
diff --git a/cli/config_test.go b/cli/config_test.go
new file mode 100644
index 0000000000..210743477c
--- /dev/null
+++ b/cli/config_test.go
@@ -0,0 +1,61 @@
+// Copyright 2024 Democratized Data Foundation
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+package cli
+
+import (
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+)
+
+func TestCreateConfig(t *testing.T) {
+ rootdir := t.TempDir()
+ err := createConfig(rootdir, NewDefraCommand().PersistentFlags())
+ require.NoError(t, err)
+
+ // ensure no errors when config already exists
+ err = createConfig(rootdir, NewDefraCommand().PersistentFlags())
+ require.NoError(t, err)
+
+ assert.FileExists(t, filepath.Join(rootdir, "config.yaml"))
+}
+
+func TestLoadConfigNotExist(t *testing.T) {
+ rootdir := t.TempDir()
+ cfg, err := loadConfig(rootdir, NewDefraCommand().PersistentFlags())
+ require.NoError(t, err)
+
+ assert.Equal(t, 5, cfg.GetInt("datastore.maxtxnretries"))
+
+ assert.Equal(t, filepath.Join(rootdir, "data"), cfg.GetString("datastore.badger.path"))
+ assert.Equal(t, 1<<30, cfg.GetInt("datastore.badger.valuelogfilesize"))
+ assert.Equal(t, "badger", cfg.GetString("datastore.store"))
+
+ assert.Equal(t, "127.0.0.1:9181", cfg.GetString("api.address"))
+ assert.Equal(t, []string{}, cfg.GetStringSlice("api.allowed-origins"))
+ assert.Equal(t, "", cfg.GetString("api.pubkeypath"))
+ assert.Equal(t, "", cfg.GetString("api.privkeypath"))
+
+ assert.Equal(t, false, cfg.GetBool("net.p2pdisabled"))
+ assert.Equal(t, []string{"/ip4/127.0.0.1/tcp/9171"}, cfg.GetStringSlice("net.p2paddresses"))
+ assert.Equal(t, true, cfg.GetBool("net.pubsubenabled"))
+ assert.Equal(t, false, cfg.GetBool("net.relay"))
+ assert.Equal(t, []string{}, cfg.GetStringSlice("net.peers"))
+
+ assert.Equal(t, "info", cfg.GetString("log.level"))
+ assert.Equal(t, false, cfg.GetBool("log.stacktrace"))
+ assert.Equal(t, "csv", cfg.GetString("log.format"))
+ assert.Equal(t, "stderr", cfg.GetString("log.output"))
+ assert.Equal(t, false, cfg.GetBool("log.nocolor"))
+ assert.Equal(t, false, cfg.GetBool("log.caller"))
+}
diff --git a/cli/index_create.go b/cli/index_create.go
index 099eb7e7a6..bfe5ec64c2 100644
--- a/cli/index_create.go
+++ b/cli/index_create.go
@@ -37,7 +37,7 @@ Example: create a named index for 'Users' collection on 'name' field:
defradb client index create --collection Users --fields name --name UsersByName`,
ValidArgs: []string{"collection", "fields", "name"},
RunE: func(cmd *cobra.Command, args []string) error {
- store := mustGetStoreContext(cmd)
+ store := mustGetContextStore(cmd)
var fields []client.IndexedFieldDescription
for _, name := range fieldsArg {
diff --git a/cli/index_drop.go b/cli/index_drop.go
index 03639fb277..96f007268d 100644
--- a/cli/index_drop.go
+++ b/cli/index_drop.go
@@ -28,7 +28,7 @@ Example: drop the index 'UsersByName' for 'Users' collection:
defradb client index create --collection Users --name UsersByName`,
ValidArgs: []string{"collection", "name"},
RunE: func(cmd *cobra.Command, args []string) error {
- store := mustGetStoreContext(cmd)
+ store := mustGetContextStore(cmd)
col, err := store.GetCollectionByName(cmd.Context(), collectionArg)
if err != nil {
diff --git a/cli/index_list.go b/cli/index_list.go
index 92ada3e007..bf1fd21251 100644
--- a/cli/index_list.go
+++ b/cli/index_list.go
@@ -30,7 +30,7 @@ Example: show all index for 'Users' collection:
defradb client index list --collection Users`,
ValidArgs: []string{"collection"},
RunE: func(cmd *cobra.Command, args []string) error {
- store := mustGetStoreContext(cmd)
+ store := mustGetContextStore(cmd)
switch {
case collectionArg != "":
diff --git a/cli/init.go b/cli/init.go
deleted file mode 100644
index f9af1850b7..0000000000
--- a/cli/init.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// Copyright 2022 Democratized Data Foundation
-//
-// Use of this software is governed by the Business Source License
-// included in the file licenses/BSL.txt.
-//
-// As of the Change Date specified in that file, in accordance with
-// the Business Source License, use of this software will be governed
-// by the Apache License, Version 2.0, included in the file
-// licenses/APL.txt.
-
-package cli
-
-import (
- "fmt"
-
- "github.com/spf13/cobra"
-
- "github.com/sourcenetwork/defradb/config"
- "github.com/sourcenetwork/defradb/errors"
-)
-
-/*
-The `init` command initializes the configuration file and root directory.
-
-It covers three possible situations:
-- root dir doesn't exist
-- root dir exists and doesn't contain a config file
-- root dir exists and contains a config file
-*/
-func MakeInitCommand(cfg *config.Config) *cobra.Command {
- var reinitialize bool
- var cmd = &cobra.Command{
- Use: "init",
- Short: "Initialize DefraDB's root directory and configuration file",
- Long: `Initialize a directory for configuration and data at the given path.
-Passed flags will be persisted in the stored configuration.`,
- // Load a default configuration, considering env. variables and CLI flags.
- PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
- if err := cfg.LoadWithRootdir(false); err != nil {
- return errors.Wrap("failed to load configuration", err)
- }
- return nil
- },
- RunE: func(cmd *cobra.Command, args []string) error {
- if config.FolderExists(cfg.Rootdir) {
- if cfg.ConfigFileExists() {
- if reinitialize {
- if err := cfg.DeleteConfigFile(); err != nil {
- return err
- }
- if err := cfg.WriteConfigFile(); err != nil {
- return err
- }
- } else {
- log.FeedbackError(
- cmd.Context(),
- fmt.Sprintf(
- "Configuration file already exists at %v. Consider using --reinitialize",
- cfg.ConfigFilePath(),
- ),
- )
- }
- } else {
- if err := cfg.WriteConfigFile(); err != nil {
- return errors.Wrap("failed to create configuration file", err)
- }
- }
- } else {
- if err := cfg.CreateRootDirAndConfigFile(); err != nil {
- return err
- }
- }
- return nil
- },
- }
-
- cmd.Flags().BoolVar(
- &reinitialize, "reinitialize", false,
- "Reinitialize the configuration file",
- )
-
- return cmd
-}
diff --git a/cli/p2p_collection_add.go b/cli/p2p_collection_add.go
index dedae0a358..8a867e6abb 100644
--- a/cli/p2p_collection_add.go
+++ b/cli/p2p_collection_add.go
@@ -31,7 +31,7 @@ Example: add multiple collections
`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
- p2p := mustGetP2PContext(cmd)
+ p2p := mustGetContextP2P(cmd)
var collectionIDs []string
for _, id := range strings.Split(args[0], ",") {
diff --git a/cli/p2p_collection_getall.go b/cli/p2p_collection_getall.go
index 10d98582c6..8a005df801 100644
--- a/cli/p2p_collection_getall.go
+++ b/cli/p2p_collection_getall.go
@@ -22,7 +22,7 @@ func MakeP2PCollectionGetAllCommand() *cobra.Command {
This is the list of collections of the node that are synchronized on the pubsub network.`,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
- p2p := mustGetP2PContext(cmd)
+ p2p := mustGetContextP2P(cmd)
cols, err := p2p.GetAllP2PCollections(cmd.Context())
if err != nil {
diff --git a/cli/p2p_collection_remove.go b/cli/p2p_collection_remove.go
index 8aa0b5b7df..7def06e779 100644
--- a/cli/p2p_collection_remove.go
+++ b/cli/p2p_collection_remove.go
@@ -31,7 +31,7 @@ Example: remove multiple collections
`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
- p2p := mustGetP2PContext(cmd)
+ p2p := mustGetContextP2P(cmd)
var collectionIDs []string
for _, id := range strings.Split(args[0], ",") {
diff --git a/cli/p2p_replicator_delete.go b/cli/p2p_replicator_delete.go
index 6cc2ddf785..debd0ac280 100644
--- a/cli/p2p_replicator_delete.go
+++ b/cli/p2p_replicator_delete.go
@@ -32,7 +32,7 @@ Example:
`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
- p2p := mustGetP2PContext(cmd)
+ p2p := mustGetContextP2P(cmd)
var info peer.AddrInfo
if err := json.Unmarshal([]byte(args[0]), &info); err != nil {
diff --git a/cli/p2p_replicator_getall.go b/cli/p2p_replicator_getall.go
index 4bdf6e8487..0e5549fea6 100644
--- a/cli/p2p_replicator_getall.go
+++ b/cli/p2p_replicator_getall.go
@@ -25,7 +25,7 @@ Example:
defradb client p2p replicator getall
`,
RunE: func(cmd *cobra.Command, args []string) error {
- p2p := mustGetP2PContext(cmd)
+ p2p := mustGetContextP2P(cmd)
reps, err := p2p.GetAllReplicators(cmd.Context())
if err != nil {
diff --git a/cli/p2p_replicator_set.go b/cli/p2p_replicator_set.go
index 5d9c712a82..29109a920a 100644
--- a/cli/p2p_replicator_set.go
+++ b/cli/p2p_replicator_set.go
@@ -32,7 +32,7 @@ Example:
`,
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
- p2p := mustGetP2PContext(cmd)
+ p2p := mustGetContextP2P(cmd)
var info peer.AddrInfo
if err := json.Unmarshal([]byte(args[0]), &info); err != nil {
diff --git a/cli/request.go b/cli/request.go
index 56e33d7c4a..d5e37e79a3 100644
--- a/cli/request.go
+++ b/cli/request.go
@@ -45,7 +45,7 @@ with the database more conveniently.
To learn more about the DefraDB GraphQL Query Language, refer to https://docs.source.network.`,
RunE: func(cmd *cobra.Command, args []string) error {
- store := mustGetStoreContext(cmd)
+ store := mustGetContextStore(cmd)
var request string
switch {
diff --git a/cli/root.go b/cli/root.go
index 729b638f02..e4ba349f76 100644
--- a/cli/root.go
+++ b/cli/root.go
@@ -11,14 +11,10 @@
package cli
import (
- "context"
-
"github.com/spf13/cobra"
-
- "github.com/sourcenetwork/defradb/config"
)
-func MakeRootCommand(cfg *config.Config) *cobra.Command {
+func MakeRootCommand() *cobra.Command {
var cmd = &cobra.Command{
SilenceUsage: true,
Use: "defradb",
@@ -28,81 +24,108 @@ func MakeRootCommand(cfg *config.Config) *cobra.Command {
Start a DefraDB node, interact with a local or remote node, and much more.
`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
- return loadConfig(cfg)
+ if err := setContextRootDir(cmd); err != nil {
+ return err
+ }
+ return setContextConfig(cmd)
},
}
cmd.PersistentFlags().String(
- "rootdir", "",
- "Directory for data and configuration to use (default: $HOME/.defradb)",
+ "rootdir",
+ "",
+ "Directory for persistent data (default: $HOME/.defradb)",
)
- err := cfg.BindFlag(config.RootdirKey, cmd.PersistentFlags().Lookup("rootdir"))
- if err != nil {
- log.FeedbackFatalE(context.Background(), "Could not bind rootdir", err)
- }
cmd.PersistentFlags().String(
- "loglevel", cfg.Log.Level,
+ "loglevel",
+ "info",
"Log level to use. Options are debug, info, error, fatal",
)
- err = cfg.BindFlag("log.level", cmd.PersistentFlags().Lookup("loglevel"))
- if err != nil {
- log.FeedbackFatalE(context.Background(), "Could not bind log.loglevel", err)
- }
-
- cmd.PersistentFlags().StringArray(
- "logger", []string{},
- "Override logger parameters. Usage: --logger ,level=,output=