Compare commits
41 Commits
storage@0.
...
@nhost/das
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
be8f4e5b1b | ||
|
|
010573cc31 | ||
|
|
629bbe7a78 | ||
|
|
166889be1b | ||
|
|
c80f6292c6 | ||
|
|
5c7a6788b4 | ||
|
|
6ae4e17ffe | ||
|
|
515fde79a3 | ||
|
|
545d0e33d9 | ||
|
|
9d1853742e | ||
|
|
c0beb07b77 | ||
|
|
3a79db6277 | ||
|
|
3378739967 | ||
|
|
e31ac82a55 | ||
|
|
bdd88161c6 | ||
|
|
17e8acb368 | ||
|
|
a2bc1fee6f | ||
|
|
ba2ac461e1 | ||
|
|
d2cc79e838 | ||
|
|
31a30cd460 | ||
|
|
f5ecbdac22 | ||
|
|
565aee6d34 | ||
|
|
47013da462 | ||
|
|
2e701456d3 | ||
|
|
f08bbc62f6 | ||
|
|
93c233deb0 | ||
|
|
ff2a84aa37 | ||
|
|
3ca082d368 | ||
|
|
cec16c6b89 | ||
|
|
543f2c2b0e | ||
|
|
53ac9263c1 | ||
|
|
81b304a715 | ||
|
|
ac9956bcdb | ||
|
|
7d2bc4c06e | ||
|
|
48ef43202c | ||
|
|
e6ae494336 | ||
|
|
092e98358f | ||
|
|
dd945daa1a | ||
|
|
0fe38e206b | ||
|
|
373657339c | ||
|
|
3833158107 |
41
.github/actions/validate-pr-title/action.yaml
vendored
Normal file
41
.github/actions/validate-pr-title/action.yaml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: "Validate PR Title"
|
||||
description: "Validates that PR title follows the required format: TYPE(PKG): SUMMARY"
|
||||
inputs:
|
||||
pr_title:
|
||||
description: "The PR title to validate"
|
||||
required: true
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: "Validate PR title format"
|
||||
shell: bash
|
||||
run: |
|
||||
PR_TITLE="${{ inputs.pr_title }}"
|
||||
|
||||
echo "Validating PR title: $PR_TITLE"
|
||||
|
||||
# Define valid types and packages
|
||||
VALID_TYPES="feat|fix|chore"
|
||||
VALID_PKGS="ci|cli|codegen|dashboard|deps|docs|examples|mintlify-openapi|nhost-js|nixops|storage"
|
||||
|
||||
# Check if title matches the pattern TYPE(PKG): SUMMARY
|
||||
if [[ ! "$PR_TITLE" =~ ^(${VALID_TYPES})\((${VALID_PKGS})\):\ .+ ]]; then
|
||||
echo "❌ PR title does not follow the required format!"
|
||||
echo ""
|
||||
echo "Expected format: TYPE(PKG): SUMMARY"
|
||||
echo ""
|
||||
echo "Valid TYPEs:"
|
||||
echo " - feat: mark this pull request as a feature"
|
||||
echo " - fix: mark this pull request as a bug fix"
|
||||
echo " - chore: mark this pull request as a maintenance item"
|
||||
echo ""
|
||||
echo "Valid PKGs:"
|
||||
echo " - ci, cli, codegen, dashboard, deps, docs, examples,"
|
||||
echo " - mintlify-openapi, nhost-js, nixops, storage"
|
||||
echo ""
|
||||
echo "Example: feat(cli): add new command for database migrations"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ PR title is valid!"
|
||||
6
.github/workflows/ci_release.yaml
vendored
6
.github/workflows/ci_release.yaml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
cli:
|
||||
needs: extract-project
|
||||
if: needs.extract-project.outputs.project == 'cli'
|
||||
uses: ./.github/workflows/cli_release.yaml
|
||||
uses: ./.github/workflows/cli_wf_release.yaml
|
||||
with:
|
||||
GIT_REF: ${{ github.sha }}
|
||||
VERSION: ${{ needs.extract-project.outputs.version }}
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
dashboard:
|
||||
needs: extract-project
|
||||
if: needs.extract-project.outputs.project == '@nhost/dashboard'
|
||||
uses: ./.github/workflows/dashboard_release.yaml
|
||||
uses: ./.github/workflows/dashboard_wf_release.yaml
|
||||
with:
|
||||
GIT_REF: ${{ github.sha }}
|
||||
VERSION: ${{ needs.extract-project.outputs.version }}
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
storage:
|
||||
needs: extract-project
|
||||
if: needs.extract-project.outputs.project == 'storage'
|
||||
uses: ./.github/workflows/storage_release.yaml
|
||||
uses: ./.github/workflows/storage_wf_release.yaml
|
||||
with:
|
||||
GIT_REF: ${{ github.sha }}
|
||||
VERSION: ${{ needs.extract-project.outputs.version }}
|
||||
|
||||
6
.github/workflows/cli_checks.yaml
vendored
6
.github/workflows/cli_checks.yaml
vendored
@@ -27,6 +27,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('push-{0}', github.sha) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -70,7 +74,7 @@ jobs:
|
||||
NIX_CACHE_PRIV_KEY: ${{ secrets.NIX_CACHE_PRIV_KEY }}
|
||||
|
||||
test_cli_build:
|
||||
uses: ./.github/workflows/cli_test_new_project.yaml
|
||||
uses: ./.github/workflows/cli_wf_test_new_project.yaml
|
||||
needs:
|
||||
- check-permissions
|
||||
- build_artifacts
|
||||
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
ref: ${{ inputs.GIT_REF }}
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-nhost-${{ github.event.repository.name }}
|
||||
aws-region: eu-central-1
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
comment_on_pr: false
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-nhost-${{ github.event.repository.name }}
|
||||
aws-region: eu-central-1
|
||||
4
.github/workflows/codegen_checks.yaml
vendored
4
.github/workflows/codegen_checks.yaml
vendored
@@ -25,6 +25,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('push-{0}', github.sha) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
8
.github/workflows/dashboard_checks.yaml
vendored
8
.github/workflows/dashboard_checks.yaml
vendored
@@ -32,6 +32,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('push-{0}', github.sha) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -59,6 +63,7 @@ jobs:
|
||||
VERCEL_TEAM_ID: ${{ secrets.DASHBOARD_VERCEL_TEAM_ID }}
|
||||
VERCEL_PROJECT_ID: ${{ secrets.DASHBOARD_STAGING_VERCEL_PROJECT_ID }}
|
||||
VERCEL_DEPLOY_TOKEN: ${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
|
||||
|
||||
build_artifacts:
|
||||
@@ -71,6 +76,7 @@ jobs:
|
||||
GIT_REF: ${{ github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: true
|
||||
OS_MATRIX: '["blacksmith-2vcpu-ubuntu-2404"]'
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
NIX_CACHE_PUB_KEY: ${{ secrets.NIX_CACHE_PUB_KEY }}
|
||||
@@ -93,7 +99,7 @@ jobs:
|
||||
|
||||
|
||||
e2e_staging:
|
||||
uses: ./.github/workflows/wf_dashboard_e2e_staging.yaml
|
||||
uses: ./.github/workflows/dashboard_wf_e2e_staging.yaml
|
||||
needs:
|
||||
- check-permissions
|
||||
- deploy-vercel
|
||||
|
||||
27
.github/workflows/dashboard_release_staging.yaml
vendored
27
.github/workflows/dashboard_release_staging.yaml
vendored
@@ -4,6 +4,32 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- '.github/workflows/wf_build_artifacts.yaml'
|
||||
- '.github/workflows/wf_check.yaml'
|
||||
- '.github/workflows/dashboard_checks.yaml'
|
||||
|
||||
# common build
|
||||
- 'flake.nix'
|
||||
- 'flake.lock'
|
||||
- 'nixops/**'
|
||||
- 'build/**'
|
||||
|
||||
# common javascript
|
||||
- ".npmrc"
|
||||
- ".prettierignore"
|
||||
- ".prettierrc.js"
|
||||
- "audit-ci.jsonc"
|
||||
- "package.json"
|
||||
- "pnpm-workspace.yaml"
|
||||
- "pnpm-lock.yaml"
|
||||
- "turbo.json"
|
||||
|
||||
# dashboard
|
||||
- "dashboard/**"
|
||||
|
||||
# nhost-js
|
||||
- packages/nhost-js/**
|
||||
|
||||
jobs:
|
||||
deploy-vercel:
|
||||
@@ -19,4 +45,5 @@ jobs:
|
||||
VERCEL_TEAM_ID: ${{ secrets.DASHBOARD_VERCEL_TEAM_ID }}
|
||||
VERCEL_PROJECT_ID: ${{ secrets.DASHBOARD_STAGING_VERCEL_PROJECT_ID }}
|
||||
VERCEL_DEPLOY_TOKEN: ${{ secrets.DASHBOARD_VERCEL_DEPLOY_TOKEN }}
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK_STAGING }}
|
||||
|
||||
@@ -59,6 +59,10 @@ on:
|
||||
PLAYWRIGHT_REPORT_ENCRYPTION_KEY:
|
||||
required: true
|
||||
|
||||
concurrency:
|
||||
group: dashboard-e2e-staging
|
||||
cancel-in-progress: false
|
||||
|
||||
env:
|
||||
NEXT_PUBLIC_ENV: dev
|
||||
NEXT_TELEMETRY_DISABLED: 1
|
||||
@@ -101,7 +105,7 @@ jobs:
|
||||
comment_on_pr: false
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-nhost-${{ github.event.repository.name }}
|
||||
aws-region: eu-central-1
|
||||
@@ -46,6 +46,7 @@ jobs:
|
||||
VERCEL_TEAM_ID: ${{ secrets.VERCEL_TEAM_ID }}
|
||||
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||
VERCEL_DEPLOY_TOKEN: ${{ secrets.VERCEL_DEPLOY_TOKEN }}
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}
|
||||
|
||||
build_artifacts:
|
||||
@@ -97,6 +98,7 @@ jobs:
|
||||
committer: GitHub <noreply@github.com>
|
||||
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
|
||||
body: |
|
||||
This PR bumps the Nhost Dashboard Docker image to version ${{ needs.version.outputs.dashboardVersion }}.
|
||||
This PR bumps the Nhost Dashboard Docker image to version ${{ inputs.VERSION }}.
|
||||
branch: bump-dashboard-version
|
||||
base: main
|
||||
delete-branch: true
|
||||
7
.github/workflows/docs_checks.yaml
vendored
7
.github/workflows/docs_checks.yaml
vendored
@@ -27,10 +27,17 @@ on:
|
||||
|
||||
# nhost-js
|
||||
- packages/nhost-js/**
|
||||
|
||||
# cli
|
||||
- cli/**
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('push-{0}', github.sha) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
5
.github/workflows/examples_demos_checks.yaml
vendored
5
.github/workflows/examples_demos_checks.yaml
vendored
@@ -41,6 +41,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('push-{0}', github.sha) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -77,6 +81,7 @@ jobs:
|
||||
GIT_REF: ${{ github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: false
|
||||
OS_MATRIX: '["blacksmith-2vcpu-ubuntu-2404"]'
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
NIX_CACHE_PUB_KEY: ${{ secrets.NIX_CACHE_PUB_KEY }}
|
||||
|
||||
@@ -41,6 +41,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('push-{0}', github.sha) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -77,6 +81,7 @@ jobs:
|
||||
GIT_REF: ${{ github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: false
|
||||
OS_MATRIX: '["blacksmith-2vcpu-ubuntu-2404"]'
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
NIX_CACHE_PUB_KEY: ${{ secrets.NIX_CACHE_PUB_KEY }}
|
||||
|
||||
@@ -41,6 +41,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('push-{0}', github.sha) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -77,6 +81,7 @@ jobs:
|
||||
GIT_REF: ${{ github.sha }}
|
||||
VERSION: 0.0.0-dev # we use a fixed version here to avoid unnecessary rebuilds
|
||||
DOCKER: false
|
||||
OS_MATRIX: '["blacksmith-2vcpu-ubuntu-2404"]'
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID: ${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}
|
||||
NIX_CACHE_PUB_KEY: ${{ secrets.NIX_CACHE_PUB_KEY }}
|
||||
|
||||
5
.github/workflows/gen_ai_review.yaml
vendored
5
.github/workflows/gen_ai_review.yaml
vendored
@@ -21,6 +21,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
OPENAI_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
config.model: "anthropic/claude-sonnet-4-20250514"
|
||||
config.model_turbo: "anthropic/claude-sonnet-4-20250514"
|
||||
config.model: ${{ vars.GEN_AI_MODEL }}
|
||||
config.model_turbo: $${{ vars.GEN_AI_MODEL_TURBO }}
|
||||
config.max_model_tokens: 200000
|
||||
ignore.glob: "['pnpm-lock.yaml','**/pnpm-lock.yaml', 'vendor/**','**/client_gen.go','**/models_gen.go','**/generated.go','**/*.gen.go']"
|
||||
|
||||
10
.github/workflows/gen_codeql-analysis.yml
vendored
10
.github/workflows/gen_codeql-analysis.yml
vendored
@@ -1,8 +1,6 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push: {}
|
||||
pull_request: {}
|
||||
schedule:
|
||||
- cron: '20 23 * * 3'
|
||||
|
||||
@@ -18,7 +16,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'javascript' ]
|
||||
language: [ 'javascript', 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
@@ -28,7 +26,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -39,7 +37,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v3
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -53,4 +51,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
||||
@@ -18,12 +18,12 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}:role/github-actions-nhost-${{ github.event.repository.name }}
|
||||
aws-region: eu-central-1
|
||||
|
||||
- uses: nixbuild/nix-quick-install-action@v26
|
||||
- uses: nixbuild/nix-quick-install-action@v34
|
||||
with:
|
||||
nix_version: 2.16.2
|
||||
nix_conf: |
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
"
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: Update dependencies
|
||||
|
||||
4
.github/workflows/nhost-js_checks.yaml
vendored
4
.github/workflows/nhost-js_checks.yaml
vendored
@@ -38,6 +38,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('push-{0}', github.sha) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
4
.github/workflows/nixops_checks.yaml
vendored
4
.github/workflows/nixops_checks.yaml
vendored
@@ -17,6 +17,10 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('push-{0}', github.sha) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
6
.github/workflows/storage_checks.yaml
vendored
6
.github/workflows/storage_checks.yaml
vendored
@@ -21,11 +21,15 @@ on:
|
||||
- 'vendor/**'
|
||||
|
||||
# storage
|
||||
- 'storage/**'
|
||||
- 'services/storage/**'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('push-{0}', github.sha) }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
check-permissions:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
14
.github/workflows/wf_build_artifacts.yaml
vendored
14
.github/workflows/wf_build_artifacts.yaml
vendored
@@ -17,6 +17,10 @@ on:
|
||||
DOCKER:
|
||||
type: boolean
|
||||
required: true
|
||||
OS_MATRIX:
|
||||
type: string
|
||||
required: false
|
||||
default: '["blacksmith-4vcpu-ubuntu-2404-arm", "blacksmith-2vcpu-ubuntu-2404"]'
|
||||
secrets:
|
||||
AWS_ACCOUNT_ID:
|
||||
required: true
|
||||
@@ -37,7 +41,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [blacksmith-4vcpu-ubuntu-2404-arm, blacksmith-2vcpu-ubuntu-2404]
|
||||
os: ${{ fromJSON(inputs.OS_MATRIX) }}
|
||||
fail-fast: true
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -49,8 +53,14 @@ jobs:
|
||||
with:
|
||||
ref: ${{ inputs.GIT_REF }}
|
||||
|
||||
- name: "Validate PR title"
|
||||
uses: ./.github/actions/validate-pr-title
|
||||
with:
|
||||
pr_title: ${{ github.event.pull_request.title }}
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-nhost-${{ github.event.repository.name }}
|
||||
aws-region: eu-central-1
|
||||
|
||||
8
.github/workflows/wf_check.yaml
vendored
8
.github/workflows/wf_check.yaml
vendored
@@ -44,13 +44,19 @@ jobs:
|
||||
with:
|
||||
ref: ${{ inputs.GIT_REF }}
|
||||
|
||||
- name: "Validate PR title"
|
||||
uses: ./.github/actions/validate-pr-title
|
||||
with:
|
||||
pr_title: ${{ github.event.pull_request.title }}
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
- name: Collect Workflow Telemetry
|
||||
uses: catchpoint/workflow-telemetry-action@v2
|
||||
with:
|
||||
comment_on_pr: false
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-nhost-${{ github.event.repository.name }}
|
||||
aws-region: eu-central-1
|
||||
|
||||
6
.github/workflows/wf_deploy_vercel.yaml
vendored
6
.github/workflows/wf_deploy_vercel.yaml
vendored
@@ -27,6 +27,8 @@ on:
|
||||
required: true
|
||||
DISCORD_WEBHOOK:
|
||||
required: false
|
||||
TURBO_TOKEN:
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
preview-url:
|
||||
@@ -52,7 +54,7 @@ jobs:
|
||||
ref: ${{ inputs.GIT_REF }}
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-nhost-${{ github.event.repository.name }}
|
||||
aws-region: eu-central-1
|
||||
@@ -69,6 +71,8 @@ jobs:
|
||||
env:
|
||||
VERCEL_ORG_ID: ${{ secrets.VERCEL_TEAM_ID }}
|
||||
VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: nhost
|
||||
run: |
|
||||
TARGET_OPTS="--target=${{ inputs.ENVIRONMENT }}"
|
||||
echo "Deploying to: ${{ inputs.ENVIRONMENT }}..."
|
||||
|
||||
2
.github/workflows/wf_docker_push_image.yaml
vendored
2
.github/workflows/wf_docker_push_image.yaml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Check out repository"
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
@@ -33,13 +33,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: "Check out repository"
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-nhost-${{ github.event.repository.name }}
|
||||
aws-region: eu-central-1
|
||||
|
||||
2
.github/workflows/wf_release_npm.yaml
vendored
2
.github/workflows/wf_release_npm.yaml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
uses: aws-actions/configure-aws-credentials@v5
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/github-actions-nhost-${{ github.event.repository.name }}
|
||||
aws-region: eu-central-1
|
||||
|
||||
20
cli/.github/PULL_REQUEST_TEMPLATE.md
vendored
20
cli/.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,20 +0,0 @@
|
||||
## Description
|
||||
<!--
|
||||
Use one of the following title prefix to categorize the pull request:
|
||||
feat: mark this pull request as a feature
|
||||
fix: mark this pull request as a bug fix
|
||||
chore: mark this pull request as a maintenance item
|
||||
|
||||
To auto merge this pull request when it was approved
|
||||
by another member of the organization: set the label `auto-merge`
|
||||
-->
|
||||
## Problem
|
||||
A short description of the problem this PR is addressing.
|
||||
|
||||
## Solution
|
||||
A short description of the chosen method to resolve the problem
|
||||
with an overview of the logic and implementation details when needed.
|
||||
|
||||
## Notes
|
||||
Other notes that you want to share but do not fit into _Problem_ or _Solution_.
|
||||
|
||||
36
cli/.github/cert.sh
vendored
36
cli/.github/cert.sh
vendored
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
mkdir -p /tmp/letsencrypt
|
||||
|
||||
echo "Generating SSL certificate for hostnames: local.nhost.run, local.graphql.nhost.run, local.auth.nhost.run, local.storage.nhost.run, local.functions.nhost.run, local.mail.nhost.run"
|
||||
docker run --rm \
|
||||
--name certbot \
|
||||
-e AWS_ACCESS_KEY_ID \
|
||||
-e AWS_SECRET_ACCESS_KEY \
|
||||
-e AWS_SESSION_TOKEN \
|
||||
-e AWS_REGION \
|
||||
-v /tmp/letsencrypt:/etc/letsencrypt \
|
||||
-v /tmp/letsencrypt:/var/lib/letsencrypt \
|
||||
certbot/dns-route53 certonly --dns-route53 --dns-route53-propagation-seconds 60 \
|
||||
-d local.auth.nhost.run \
|
||||
-d local.dashboard.nhost.run \
|
||||
-d local.db.nhost.run \
|
||||
-d local.functions.nhost.run \
|
||||
-d local.graphql.nhost.run \
|
||||
-d local.hasura.nhost.run \
|
||||
-d local.mailhog.nhost.run \
|
||||
-d local.storage.nhost.run \
|
||||
-d *.auth.local.nhost.run \
|
||||
-d *.dashboard.local.nhost.run \
|
||||
-d *.db.local.nhost.run \
|
||||
-d *.functions.local.nhost.run \
|
||||
-d *.graphql.local.nhost.run \
|
||||
-d *.hasura.local.nhost.run \
|
||||
-d *.mailhog.local.nhost.run \
|
||||
-d *.storage.local.nhost.run \
|
||||
-m 'admin@nhost.io' --non-interactive --agree-tos --server https://acme-v02.api.letsencrypt.org/directory
|
||||
|
||||
sudo cp /tmp/letsencrypt/live/local.db.nhost.run/fullchain.pem ssl/.ssl/
|
||||
sudo cp /tmp/letsencrypt/live/local.db.nhost.run/privkey.pem ssl/.ssl/
|
||||
8
cli/.github/labeler.yml
vendored
8
cli/.github/labeler.yml
vendored
@@ -1,8 +0,0 @@
|
||||
labels:
|
||||
'feature':
|
||||
- '^(?i:feat)'
|
||||
- '^(?i:feature)'
|
||||
'fix':
|
||||
- '^(?i:fix)'
|
||||
'chore':
|
||||
- '^(?i:chore)'
|
||||
39
cli/.github/release-drafter.yml
vendored
39
cli/.github/release-drafter.yml
vendored
@@ -1,39 +0,0 @@
|
||||
name-template: 'v$RESOLVED_VERSION'
|
||||
tag-template: 'v$RESOLVED_VERSION'
|
||||
categories:
|
||||
- title: '🚀 Features'
|
||||
label: 'feature'
|
||||
- title: '🐛 Bug Fixes'
|
||||
label: 'fix'
|
||||
- title: '🧰 Maintenance'
|
||||
label: 'chore'
|
||||
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
|
||||
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
|
||||
version-resolver:
|
||||
major:
|
||||
labels:
|
||||
- 'major'
|
||||
minor:
|
||||
labels:
|
||||
- 'minor'
|
||||
patch:
|
||||
labels:
|
||||
- 'patch'
|
||||
default: patch
|
||||
autolabeler:
|
||||
- label: 'feature'
|
||||
title:
|
||||
- '/^feat/i'
|
||||
- '/^feature/i'
|
||||
- label: 'fix'
|
||||
title:
|
||||
- '/^fix/i'
|
||||
- label: 'chore'
|
||||
title:
|
||||
- '/^chore/i'
|
||||
prerelease: true
|
||||
template: |
|
||||
## Changes
|
||||
|
||||
$CHANGES
|
||||
|
||||
16
cli/.github/stale.yml
vendored
16
cli/.github/stale.yml
vendored
@@ -1,16 +0,0 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
daysUntilStale: 180
|
||||
daysUntilClose: 7
|
||||
limitPerRun: 30
|
||||
onlyLabels: []
|
||||
exemptLabels: []
|
||||
|
||||
exemptProjects: false
|
||||
exemptMilestones: false
|
||||
exemptAssignees: false
|
||||
staleLabel: stale
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
17
cli/.github/workflows/assign_labels.yml
vendored
17
cli/.github/workflows/assign_labels.yml
vendored
@@ -1,17 +0,0 @@
|
||||
# this workflow will run on all pull requests opened but in the context of the base of the pull request.
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
|
||||
name: "assign labels"
|
||||
jobs:
|
||||
# labeler will label pull requests based on their title.
|
||||
# the configuration is at .github/labeler.yml.
|
||||
label_pull_request:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Label Pull Request
|
||||
uses: jimschubert/labeler-action@v2
|
||||
with:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
@@ -1,53 +0,0 @@
|
||||
---
|
||||
name: "build certificate weekly"
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 1'
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v2
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::796351718684:role/github-actions-nhost-cli
|
||||
aws-region: eu-central-1
|
||||
|
||||
- name: fetch let's encrypt cert
|
||||
id: certs
|
||||
run: |
|
||||
.github/cert.sh
|
||||
|
||||
echo "CERT_FULL_CHAIN<<EOF" >> $GITHUB_OUTPUT
|
||||
sudo cat /tmp/letsencrypt/live/local.db.nhost.run/fullchain.pem >> "$GITHUB_OUTPUT"
|
||||
echo EOF >> $GITHUB_OUTPUT
|
||||
|
||||
echo "CERT_PRIV_KEY<<EOF" >> $GITHUB_OUTPUT
|
||||
sudo cat /tmp/letsencrypt/live/local.db.nhost.run/privkey.pem >> "$GITHUB_OUTPUT"
|
||||
echo EOF >> $GITHUB_OUTPUT
|
||||
shell: bash
|
||||
|
||||
- uses: hmanzur/actions-set-secret@v2.0.0
|
||||
with:
|
||||
name: 'CERT_FULL_CHAIN'
|
||||
value: "${{ steps.certs.outputs.CERT_FULL_CHAIN }}"
|
||||
repository: nhost/cli
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
|
||||
- uses: hmanzur/actions-set-secret@v2.0.0
|
||||
with:
|
||||
name: 'CERT_PRIV_KEY'
|
||||
value: "${{ steps.certs.outputs.CERT_PRIV_KEY }}"
|
||||
repository: nhost/cli
|
||||
token: ${{ secrets.GH_PAT }}
|
||||
27
cli/.github/workflows/checks.yaml
vendored
27
cli/.github/workflows/checks.yaml
vendored
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: "check and build"
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
uses: ./.github/workflows/wf_check.yaml
|
||||
secrets:
|
||||
NHOST_PAT: ${{ secrets.NHOST_PAT }}
|
||||
|
||||
build_artifacts:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
GOOS: ["darwin", "linux"]
|
||||
GOARCH: ["amd64", "arm64"]
|
||||
uses: ./.github/workflows/wf_build_artifacts.yaml
|
||||
with:
|
||||
GOOS: ${{ matrix.GOOS }}
|
||||
GOARCH: ${{ matrix.GOARCH }}
|
||||
VERSION: ${{ github.sha }}
|
||||
secrets:
|
||||
NHOST_PAT: ${{ secrets.NHOST_PAT }}
|
||||
56
cli/.github/workflows/codeql-analysis.yml
vendored
56
cli/.github/workflows/codeql-analysis.yml
vendored
@@ -1,56 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push: {}
|
||||
pull_request: {}
|
||||
schedule:
|
||||
- cron: '20 23 * * 3'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
27
cli/.github/workflows/gen_ai_review.yaml
vendored
27
cli/.github/workflows/gen_ai_review.yaml
vendored
@@ -1,27 +0,0 @@
|
||||
---
|
||||
name: "gen: AI review"
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, ready_for_review]
|
||||
issue_comment:
|
||||
jobs:
|
||||
pr_agent_job:
|
||||
if: ${{ github.event.sender.type != 'Bot' }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
name: Run pr agent on every pull request, respond to user comments
|
||||
steps:
|
||||
- name: PR Agent action step
|
||||
id: pragent
|
||||
uses: Codium-ai/pr-agent@v0.29
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
OPENAI_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
config.max_model_tokens: 100000
|
||||
config.model: "anthropic/claude-sonnet-4-20250514"
|
||||
config.model_turbo: "anthropic/claude-sonnet-4-20250514"
|
||||
ignore.glob: "['vendor/**','**/client_gen.go','**/models_gen.go','**/generated.go','**/*.gen.go']"
|
||||
@@ -1,91 +0,0 @@
|
||||
---
|
||||
name: "gen: update depenendencies"
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 1 2,5,8,11 *'
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Configure aws
|
||||
uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::${{ secrets.AWS_PRODUCTION_CORE_ACCOUNT_ID }}:role/github-actions-nhost-${{ github.event.repository.name }}
|
||||
aws-region: eu-central-1
|
||||
|
||||
- uses: nixbuild/nix-quick-install-action@v26
|
||||
with:
|
||||
nix_version: 2.16.2
|
||||
nix_conf: |
|
||||
experimental-features = nix-command flakes
|
||||
sandbox = false
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
substituters = https://cache.nixos.org/?priority=40 s3://nhost-nix-cache?region=eu-central-1&priority=50
|
||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= ${{ secrets.NIX_CACHE_PUB_KEY }}
|
||||
|
||||
- name: Cache nix store
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /nix
|
||||
key: nix-update-deps-${{ hashFiles('flakes.nix', 'flake.lock') }}
|
||||
|
||||
- name: Update nix flakes
|
||||
run: nix flake update
|
||||
|
||||
- uses: shaunco/ssh-agent@git-repo-mapping
|
||||
with:
|
||||
ssh-private-key: |
|
||||
${{ secrets.NHOST_BE_DEPLOY_SSH_PRIVATE_KEY}}
|
||||
repo-mappings: |
|
||||
github.com/nhost/be
|
||||
|
||||
- name: Update golang dependencies
|
||||
run: |
|
||||
export GOPRIVATE=github.com/nhost/be
|
||||
nix develop -c bash -c "
|
||||
go mod tidy
|
||||
go get -u $(cat go.mod | grep nhost\/be | tr ' ' '@') ./...
|
||||
go mod tidy
|
||||
go mod vendor
|
||||
"
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: Update dependencies
|
||||
committer: GitHub <noreply@github.com>
|
||||
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
|
||||
signoff: false
|
||||
branch: automated/update-deps
|
||||
delete-branch: true
|
||||
title: '[Scheduled] Update dependencies'
|
||||
body: |
|
||||
Dependencies updated
|
||||
|
||||
Note - If you see this PR and the checks haven't run, close and reopen the PR. See https://github.com/peter-evans/create-pull-request/blob/main/docs/concepts-guidelines.md#triggering-further-workflow-runs
|
||||
labels: |
|
||||
dependencies
|
||||
draft: false
|
||||
|
||||
- name: "Cache nix store on s3"
|
||||
run: |
|
||||
echo ${{ secrets.NIX_CACHE_PRIV_KEY }} > cache-priv-key.pem
|
||||
nix build .\#devShells.x86_64-linux.default
|
||||
nix store sign --key-file cache-priv-key.pem --all
|
||||
nix copy --to s3://nhost-nix-cache\?region=eu-central-1 .\#devShells.x86_64-linux.default
|
||||
|
||||
- run: rm cache-priv-key.pem
|
||||
if: always()
|
||||
35
cli/.github/workflows/release.yaml
vendored
35
cli/.github/workflows/release.yaml
vendored
@@ -1,35 +0,0 @@
|
||||
---
|
||||
name: "release"
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
uses: ./.github/workflows/wf_check.yaml
|
||||
secrets:
|
||||
NHOST_PAT: ${{ secrets.NHOST_PAT }}
|
||||
|
||||
build_artifacts:
|
||||
strategy:
|
||||
matrix:
|
||||
GOOS: ["darwin", "linux"]
|
||||
GOARCH: ["amd64", "arm64"]
|
||||
uses: ./.github/workflows/wf_build_artifacts.yaml
|
||||
with:
|
||||
GOOS: ${{ matrix.GOOS }}
|
||||
GOARCH: ${{ matrix.GOARCH }}
|
||||
VERSION: ${{ github.ref_name }}
|
||||
secrets:
|
||||
NHOST_PAT: ${{ secrets.NHOST_PAT }}
|
||||
|
||||
publish:
|
||||
uses: ./.github/workflows/wf_publish.yaml
|
||||
needs:
|
||||
- tests
|
||||
- build_artifacts
|
||||
with:
|
||||
VERSION: ${{ github.ref_name }}
|
||||
secrets:
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
17
cli/.github/workflows/release_drafter.yml
vendored
17
cli/.github/workflows/release_drafter.yml
vendored
@@ -1,17 +0,0 @@
|
||||
name: "release drafter"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
# draft your next release notes as pull requests are merged into "master"
|
||||
# the configuration is at /.github/release-drafter.yml.
|
||||
update_release_draft:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: release-drafter/release-drafter@v5
|
||||
with:
|
||||
config-name: release-drafter.yml
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
89
cli/.github/workflows/wf_build_artifacts.yaml
vendored
89
cli/.github/workflows/wf_build_artifacts.yaml
vendored
@@ -1,89 +0,0 @@
|
||||
---
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
GIT_REF:
|
||||
type: string
|
||||
required: false
|
||||
VERSION:
|
||||
type: string
|
||||
required: true
|
||||
GOOS:
|
||||
type: string
|
||||
required: true
|
||||
GOARCH:
|
||||
type: string
|
||||
required: true
|
||||
secrets:
|
||||
NHOST_PAT:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
artifacts:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: "Check out repository"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.GIT_REF }}
|
||||
submodules: true
|
||||
|
||||
- uses: cachix/install-nix-action@v27
|
||||
with:
|
||||
install_url: "https://releases.nixos.org/nix/nix-2.22.3/install"
|
||||
install_options: "--no-daemon"
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
sandbox = false
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
substituters = https://cache.nixos.org/?priority=40
|
||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
|
||||
|
||||
- name: Compute common env vars
|
||||
id: vars
|
||||
run: |
|
||||
echo "VERSION=$(make get-version VERSION=${{ inputs.VERSION }})" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: "Build artifact"
|
||||
run: |
|
||||
make build ARCH=${{ inputs.GOARCH }} OS=${{ inputs.GOOS }}
|
||||
find -L result -type f -exec cp {} nhost-cli \;
|
||||
|
||||
- name: "Push artifact to artifact repository"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cli-${{ steps.vars.outputs.VERSION }}-${{ inputs.GOOS }}-${{ inputs.GOARCH }}
|
||||
path: nhost-cli
|
||||
retention-days: 7
|
||||
|
||||
- name: "Build docker-image"
|
||||
run: |
|
||||
make build-docker-image ARCH=${{ inputs.GOARCH }}
|
||||
if: ${{ ( inputs.GOOS == 'linux' ) }}
|
||||
|
||||
- name: "Create a new project"
|
||||
run: |
|
||||
export NHOST_DOMAIN=staging.nhost.run
|
||||
export NHOST_CONFIGSERVER_IMAGE=nhost/cli:${{ steps.vars.outputs.VERSION }}
|
||||
|
||||
mkdir new-project
|
||||
cd new-project
|
||||
../nhost-cli login --pat ${{ secrets.NHOST_PAT }}
|
||||
../nhost-cli init
|
||||
../nhost-cli up --down-on-error
|
||||
../nhost-cli down
|
||||
if: ${{ ( inputs.GOOS == 'linux' && inputs.GOARCH == 'amd64' ) }}
|
||||
|
||||
- name: "Push docker-image to artifact repository"
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cli-docker-image-${{ steps.vars.outputs.VERSION }}-${{ inputs.GOOS }}-${{ inputs.GOARCH }}
|
||||
path: result
|
||||
retention-days: 7
|
||||
if: ${{ ( inputs.GOOS == 'linux' ) }}
|
||||
42
cli/.github/workflows/wf_check.yaml
vendored
42
cli/.github/workflows/wf_check.yaml
vendored
@@ -1,42 +0,0 @@
|
||||
---
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
GIT_REF:
|
||||
type: string
|
||||
required: false
|
||||
secrets:
|
||||
NHOST_PAT:
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: "Check out repository"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.GIT_REF }}
|
||||
submodules: true
|
||||
|
||||
- uses: cachix/install-nix-action@v27
|
||||
with:
|
||||
install_url: "https://releases.nixos.org/nix/nix-2.22.3/install"
|
||||
install_options: "--no-daemon"
|
||||
extra_nix_config: |
|
||||
experimental-features = nix-command flakes
|
||||
sandbox = false
|
||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||
substituters = https://cache.nixos.org/?priority=40
|
||||
trusted-public-keys = cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY=
|
||||
|
||||
- name: "Run checks"
|
||||
run: |
|
||||
export NHOST_PAT=${{ secrets.NHOST_PAT }}
|
||||
make check
|
||||
93
cli/.github/workflows/wf_publish.yaml
vendored
93
cli/.github/workflows/wf_publish.yaml
vendored
@@ -1,93 +0,0 @@
|
||||
---
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
VERSION:
|
||||
type: string
|
||||
required: true
|
||||
secrets:
|
||||
DOCKER_USERNAME:
|
||||
required: true
|
||||
DOCKER_PASSWORD:
|
||||
required: true
|
||||
|
||||
name: release
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: write
|
||||
|
||||
steps:
|
||||
- name: "Check out repository"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ inputs.GIT_REF }}
|
||||
submodules: true
|
||||
|
||||
- name: Compute common env vars
|
||||
id: vars
|
||||
run: |
|
||||
echo "VERSION=$(make get-version VERSION=${{ inputs.VERSION }})" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: "Get artifacts"
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ~/artifacts
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Upload docker images
|
||||
shell: bash
|
||||
run: |
|
||||
export VERSION=${{ steps.vars.outputs.VERSION }}
|
||||
export CONTAINER_NAME=nhost/cli
|
||||
|
||||
skopeo copy --insecure-policy \
|
||||
dir:/home/runner/artifacts/cli-docker-image-$VERSION-linux-amd64 \
|
||||
docker-daemon:$CONTAINER_NAME:$VERSION-amd64
|
||||
docker push $CONTAINER_NAME:$VERSION-amd64
|
||||
|
||||
skopeo copy --insecure-policy \
|
||||
dir:/home/runner/artifacts/cli-docker-image-$VERSION-linux-arm64 \
|
||||
docker-daemon:$CONTAINER_NAME:$VERSION-arm64
|
||||
docker push $CONTAINER_NAME:$VERSION-arm64
|
||||
|
||||
docker manifest create \
|
||||
$CONTAINER_NAME:$VERSION \
|
||||
--amend $CONTAINER_NAME:$VERSION-amd64 \
|
||||
--amend $CONTAINER_NAME:$VERSION-arm64
|
||||
docker manifest push $CONTAINER_NAME:$VERSION
|
||||
|
||||
- name: Upload assets
|
||||
shell: bash
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
export VERSION=${{ steps.vars.outputs.VERSION }}
|
||||
|
||||
mkdir upload
|
||||
|
||||
find ~/artifacts -type f -name "nhost-cli" -exec bash -c 'chmod +x "$0" && mv "$0" "${0//nhost-cli/cli}"' {} \;
|
||||
|
||||
tar cvzf upload/cli-$VERSION-darwin-amd64.tar.gz -C ~/artifacts/cli-$VERSION-darwin-amd64 cli
|
||||
tar cvzf upload/cli-$VERSION-darwin-arm64.tar.gz -C ~/artifacts/cli-$VERSION-darwin-arm64 cli
|
||||
tar cvzf upload/cli-$VERSION-linux-amd64.tar.gz -C ~/artifacts/cli-$VERSION-linux-amd64 cli
|
||||
tar cvzf upload/cli-$VERSION-linux-arm64.tar.gz -C ~/artifacts/cli-$VERSION-linux-arm64 cli
|
||||
|
||||
cd upload
|
||||
find . -type f -exec sha256sum {} + > ../checksums.txt
|
||||
cd ..
|
||||
|
||||
cat checksums.txt
|
||||
|
||||
gh release upload \
|
||||
--clobber "${{ github.ref_name }}" \
|
||||
./upload/* checksums.txt
|
||||
34
cli/CHANGELOG.md
Normal file
34
cli/CHANGELOG.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [cli@1.33.0] - 2025-10-02
|
||||
|
||||
### 🚀 Features
|
||||
|
||||
- *(cli)* Migrate from urfave/v2 to urfave/v3 (#3545)
|
||||
|
||||
|
||||
### 🐛 Bug Fixes
|
||||
|
||||
- *(cli)* Disable tls on AUTH_SERVER_URL when auth uses custom port (#3549)
|
||||
- *(cli)* Fix breaking change in go-getter dependency (#3551)
|
||||
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(cli)* Update certs (#3552)
|
||||
|
||||
## [cli@1.32.2] - 2025-10-01
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(cli)* Remove hasura- prefix from auth/storage images (#3538)
|
||||
|
||||
## [cli@1.32.1] - 2025-09-29
|
||||
|
||||
### ⚙️ Miscellaneous Tasks
|
||||
|
||||
- *(ci)* Minor improvements to the ci (#3527)
|
||||
- *(cli)* Update schema (#3529)
|
||||
|
||||
199
cli/MCP.md
Normal file
199
cli/MCP.md
Normal file
@@ -0,0 +1,199 @@
|
||||
# nhost mcp
|
||||
|
||||
A Model Context Protocol (MCP) server implementation for interacting with Nhost Cloud projects and services.
|
||||
|
||||
## Overview
|
||||
|
||||
MCP-Nhost is designed to provide a unified interface for managing Nhost projects through the Model Context Protocol. It enables seamless interaction with Nhost Cloud services, offering a robust set of tools for project management and configuration.
|
||||
|
||||
## Available Tools
|
||||
|
||||
The following tools are currently exposed through the MCP interface:
|
||||
|
||||
1. **cloud-get-graphql-schema**
|
||||
- Provides the GraphQL schema for the Nhost Cloud platform
|
||||
- Gives access to queries and mutations available for cloud management
|
||||
|
||||
2. **cloud-graphql-query**
|
||||
- Executes GraphQL queries and mutations against the Nhost Cloud platform
|
||||
- Enables project and organization management
|
||||
- Allows querying and updating project configurations
|
||||
- Mutations require enabling them when starting the server
|
||||
|
||||
3. **local-get-graphql-schema**
|
||||
- Retrieves the GraphQL schema for local Nhost development projects
|
||||
- Provides access to project-specific queries and mutations
|
||||
- Helps understand available operations for local development helping generating code
|
||||
- Uses "user" role unless specified otherwise
|
||||
|
||||
4. **local-graphql-query**
|
||||
- Executes GraphQL queries against local Nhost development projects
|
||||
- Enables testing and development of project-specific operations
|
||||
- Supports both queries and mutations for local development
|
||||
- Uses "user" role unless specified otherwise
|
||||
|
||||
5. **local-config-server-get-schema**
|
||||
- Retrieves the GraphQL schema for the local config server
|
||||
- Helps understand available configuration options for local projects
|
||||
|
||||
6. **local-config-server-query**
|
||||
- Executes GraphQL queries against the local config server
|
||||
- Enables querying and modifying local project configuration
|
||||
- Changes require running 'nhost up' to take effect
|
||||
|
||||
7. **local-get-management-graphql-schema**
|
||||
- Retrieves the GraphQL management schema for local projects
|
||||
- Useful for understanding how to manage Hasura metadata, migrations, and permissions
|
||||
- Provides insight into available management operations before using the management tool
|
||||
|
||||
8. **local-manage-graphql**
|
||||
- Interacts with GraphQL's management endpoints for local projects
|
||||
- Manages Hasura metadata, migrations, permissions, and remote schemas
|
||||
- Creates and applies database migrations
|
||||
- Handles data and schema changes through proper migration workflows
|
||||
- Manages roles and permissions
|
||||
|
||||
9. **project-get-graphql-schema**
|
||||
- Retrieves the GraphQL schema for Nhost Cloud projects
|
||||
- Provides access to project-specific queries and mutations
|
||||
- Uses "user" role unless specified otherwise
|
||||
|
||||
10. **project-graphql-query**
|
||||
- Executes GraphQL queries against Nhost Cloud projects
|
||||
- Enables interaction with live project data
|
||||
- Supports both queries and mutations (need to be allowed)
|
||||
- Uses "user" role unless specified otherwise
|
||||
|
||||
11. **search**
|
||||
- Searches Nhost's official documentation
|
||||
- Provides information about Nhost features, APIs, and guides
|
||||
- Helps find relevant documentation for implementing features or solving issues
|
||||
- Returns links to detailed documentation pages
|
||||
|
||||
## Screenshots and Examples
|
||||
|
||||
You can find screenshots and examples of the current features and tools in the [screenshots](docs/mcp/screenshots.md) file.
|
||||
|
||||
## Installing
|
||||
|
||||
To install mcp-nhost, you can use the following command:
|
||||
|
||||
```bash
|
||||
sudo curl -L https://raw.githubusercontent.com/nhost/mcp-nhost/main/get.sh | bash
|
||||
```
|
||||
|
||||
## Configuring
|
||||
|
||||
After installing mcp-nhost, you will need to configure it. You can do this by running the command `mcp-nhost config` in your terminal. See [CONFIG.md](docs/mcp/CONFIG.md) for more details.
|
||||
|
||||
## Configuring clients
|
||||
|
||||
#### Cursor
|
||||
|
||||
1. Go to "Cursor Settings"
|
||||
2. Click on "MCP"
|
||||
3. Click on "+ Add new global MCP server"
|
||||
4. Add the following object inside `"mcpServers"`:
|
||||
|
||||
```json
|
||||
"mcp-nhost": {
|
||||
"command": "/usr/local/bin/mcp-nhost",
|
||||
"args": [
|
||||
"start",
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
## CLI Usage
|
||||
|
||||
For help on how to use the CLI, you can run:
|
||||
|
||||
```bash
|
||||
mcp-nhost --help
|
||||
```
|
||||
|
||||
Or check [USAGE.md](docs/mcp/USAGE.md) for more details.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If you run into issues using the MCP server you can try running the tools yourself. For example:
|
||||
|
||||
```
|
||||
# cloud-get-graphql-schema
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"cloud-get-graphql-schema","arguments":{}},"id":1}' | mcp-nhost start
|
||||
|
||||
# cloud-graphql-query
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"cloud-graphql-query","arguments":{"query":"{ apps { id subdomain name } }"}},"id":1}' | mcp-nhost start
|
||||
|
||||
# local-get-graphql-schema
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"local-get-graphql-schema","arguments":{"role":"user"}},"id":1}' | mcp-nhost start
|
||||
|
||||
# local-graphql-query
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"local-graphql-query","arguments":{"query":"{ users { id } }", "role":"admin"}},"id":1}' | mcp-nhost start
|
||||
|
||||
# local-config-server-get-schema
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"local-config-server-get-schema","arguments":{}},"id":1}' | mcp-nhost start
|
||||
|
||||
# local-config-server-query
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"local-config-server-query","arguments":{"query":"{ config(appID: \"00000000-0000-0000-0000-000000000000\", resolve: true) { postgres { version } } }"}},"id":1}' | mcp-nhost start
|
||||
|
||||
# local-get-management-graphql-schema
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"local-get-management-graphql-schema","arguments":{}},"id":1}' | mcp-nhost start
|
||||
|
||||
# local-manage-graphql
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"local-manage-graphql","arguments":{"body":"{\"type\":\"export_metadata\",\"args\":{}}","endpoint":"https://local.hasura.local.nhost.run/v1/metadata"}},"id":1}' | mcp-nhost start
|
||||
|
||||
# project-get-graphql-schema - set projectSubdomain to your own project
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"project-get-graphql-schema","arguments":{"projectSubdomain":"replaceMe", "role": "user"}},"id":1}' | mcp-nhost start
|
||||
|
||||
# project-graphql-query - set projectSubdomain to your own project
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"project-graphql-query","arguments":{"projectSubdomain":"replaceMe","query":"{ users { id } }", "role":"admin"}},"id":1}' | mcp-nhost start
|
||||
|
||||
# search
|
||||
echo '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"search","arguments":{"query":"how to enable magic links"}},"id":1}' | mcp-nhost start
|
||||
```
|
||||
|
||||
## Roadmap
|
||||
|
||||
- ✅ Cloud platform: Basic project and organization management
|
||||
- ✅ Cloud projects: Configuration management
|
||||
- ✅ Local projects: Configuration management
|
||||
- ✅ Local projects: Graphql Schema awareness and query execution
|
||||
- ✅ Cloud projects: Schema awareness and query execution
|
||||
- ✅ Local projects: Create migrations
|
||||
- ✅ Local projects: Manage permissions and relationships
|
||||
- ✅ Documentation: integrate or document use of mintlify's mcp server
|
||||
- ✅ Local projects: Auth and Storage schema awareness (maybe via mintlify?)
|
||||
- ✅ Cloud projects: Auth and Storage schema awareness (maybe via mintlify?)
|
||||
- 🔄 Local projects: Manage more metadata
|
||||
|
||||
If you have any suggestions or feature requests, please feel free to open an issue for discussion.
|
||||
|
||||
## Security and Privacy
|
||||
|
||||
### Enhanced Protection Layer
|
||||
|
||||
The MCP server is designed with security at its core, providing an additional protection layer beyond your existing GraphQL permissions. Key security features include:
|
||||
|
||||
- **Authentication enforcement** for all requests
|
||||
- **Permission and role respect** based on your existing authorization system and the credentials provided
|
||||
- **Query/mutation filtering** to further restrict allowed operations
|
||||
|
||||
### Granular Access Control
|
||||
|
||||
One of the MCP server's key security advantages is the ability to specify exactly which operations can pass through, even for authenticated users:
|
||||
|
||||
```toml
|
||||
[[projects]]
|
||||
subdomain = "my-blog"
|
||||
region = "eu-central-1"
|
||||
pat = "nhp_project_specific_pat"
|
||||
allow_queries = ["getBlogs", "getCommends"]
|
||||
allow_mutations = ["insertBlog", "insertComment"]
|
||||
```
|
||||
|
||||
With the configuration above, an LLM will be able to only execute the queries and mutations above on behalf of a user even if the user has broader permissions in the Nhost project.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions to mcp-nhost! If you have suggestions, bug reports, or feature requests, please open an issue or submit a pull request.
|
||||
@@ -12,9 +12,9 @@ It's recommended to use the Nhost CLI and the [Nhost GitHub Integration](https:/
|
||||
|
||||
- [Nhost Dashboard](https://github.com/nhost/nhost/tree/main/dashboard)
|
||||
- [Postgres Database](https://www.postgresql.org/)
|
||||
- [Hasura's GraphQL Engine](https://github.com/hasura/graphql-engine)
|
||||
- [Hasura Auth](https://github.com/nhost/hasura-auth)
|
||||
- [Hasura Storage](https://github.com/nhost/hasura-storage)
|
||||
- [GraphQL Engine](https://github.com/hasura/graphql-engine)
|
||||
- [Auth](https://github.com/nhost/nhost/main/auth)
|
||||
- [Storage](https://github.com/nhost/nhost/main/storage)
|
||||
- [Nhost Serverless Functions](https://github.com/nhost/functions)
|
||||
- [Minio S3](https://github.com/minio/minio)
|
||||
- [Mailhog](https://github.com/mailhog/MailHog)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/nhost/nhost/cli/nhostclient"
|
||||
"github.com/nhost/nhost/cli/nhostclient/graphql"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func sanitizeName(name string) string {
|
||||
@@ -55,28 +55,28 @@ func New(
|
||||
}
|
||||
}
|
||||
|
||||
func FromCLI(cCtx *cli.Context) *CliEnv {
|
||||
func FromCLI(cmd *cli.Command) *CliEnv {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return &CliEnv{
|
||||
stdout: cCtx.App.Writer,
|
||||
stderr: cCtx.App.ErrWriter,
|
||||
stdout: cmd.Writer,
|
||||
stderr: cmd.ErrWriter,
|
||||
Path: NewPathStructure(
|
||||
cwd,
|
||||
cCtx.String(flagRootFolder),
|
||||
cCtx.String(flagDotNhostFolder),
|
||||
cCtx.String(flagNhostFolder),
|
||||
cmd.String(flagRootFolder),
|
||||
cmd.String(flagDotNhostFolder),
|
||||
cmd.String(flagNhostFolder),
|
||||
),
|
||||
authURL: cCtx.String(flagAuthURL),
|
||||
graphqlURL: cCtx.String(flagGraphqlURL),
|
||||
branch: cCtx.String(flagBranch),
|
||||
projectName: sanitizeName(cCtx.String(flagProjectName)),
|
||||
authURL: cmd.String(flagAuthURL),
|
||||
graphqlURL: cmd.String(flagGraphqlURL),
|
||||
branch: cmd.String(flagBranch),
|
||||
projectName: sanitizeName(cmd.String(flagProjectName)),
|
||||
nhclient: nil,
|
||||
nhpublicclient: nil,
|
||||
localSubdomain: cCtx.String(flagLocalSubdomain),
|
||||
localSubdomain: cmd.String(flagLocalSubdomain),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -53,42 +53,42 @@ func Flags() ([]cli.Flag, error) {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagAuthURL,
|
||||
Usage: "Nhost auth URL",
|
||||
EnvVars: []string{"NHOST_CLI_AUTH_URL"},
|
||||
Sources: cli.EnvVars("NHOST_CLI_AUTH_URL"),
|
||||
Value: "https://otsispdzcwxyqzbfntmj.auth.eu-central-1.nhost.run/v1",
|
||||
Hidden: true,
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagGraphqlURL,
|
||||
Usage: "Nhost GraphQL URL",
|
||||
EnvVars: []string{"NHOST_CLI_GRAPHQL_URL"},
|
||||
Sources: cli.EnvVars("NHOST_CLI_GRAPHQL_URL"),
|
||||
Value: "https://otsispdzcwxyqzbfntmj.graphql.eu-central-1.nhost.run/v1",
|
||||
Hidden: true,
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagBranch,
|
||||
Usage: "Git branch name. If not set, it will be detected from the current git repository. This flag is used to dynamically create docker volumes for each branch. If you want to have a static volume name or if you are not using git, set this flag to a static value.", //nolint:lll
|
||||
EnvVars: []string{"BRANCH"},
|
||||
Sources: cli.EnvVars("BRANCH"),
|
||||
Value: branch,
|
||||
Hidden: false,
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagRootFolder,
|
||||
Usage: "Root folder of project\n\t",
|
||||
EnvVars: []string{"NHOST_ROOT_FOLDER"},
|
||||
Sources: cli.EnvVars("NHOST_ROOT_FOLDER"),
|
||||
Value: workingDir,
|
||||
Category: "Project structure",
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagDotNhostFolder,
|
||||
Usage: "Path to .nhost folder\n\t",
|
||||
EnvVars: []string{"NHOST_DOT_NHOST_FOLDER"},
|
||||
Sources: cli.EnvVars("NHOST_DOT_NHOST_FOLDER"),
|
||||
Value: dotNhostFolder,
|
||||
Category: "Project structure",
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagNhostFolder,
|
||||
Usage: "Path to nhost folder\n\t",
|
||||
EnvVars: []string{"NHOST_NHOST_FOLDER"},
|
||||
Sources: cli.EnvVars("NHOST_NHOST_FOLDER"),
|
||||
Value: nhostFolder,
|
||||
Category: "Project structure",
|
||||
},
|
||||
@@ -96,13 +96,13 @@ func Flags() ([]cli.Flag, error) {
|
||||
Name: flagProjectName,
|
||||
Usage: "Project name",
|
||||
Value: filepath.Base(fullWorkingDir),
|
||||
EnvVars: []string{"NHOST_PROJECT_NAME"},
|
||||
Sources: cli.EnvVars("NHOST_PROJECT_NAME"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagLocalSubdomain,
|
||||
Usage: "Local subdomain to reach the development environment",
|
||||
Value: "local",
|
||||
EnvVars: []string{"NHOST_LOCAL_SUBDOMAIN"},
|
||||
Sources: cli.EnvVars("NHOST_LOCAL_SUBDOMAIN"),
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -29,3 +29,12 @@ func (ce *CliEnv) LoadSession(
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (ce *CliEnv) Credentials() (credentials.Credentials, error) {
|
||||
var creds credentials.Credentials
|
||||
if err := UnmarshalFile(ce.Path.AuthFile(), &creds, json.Unmarshal); err != nil {
|
||||
return credentials.Credentials{}, err
|
||||
}
|
||||
|
||||
return creds, nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandApply() *cli.Command {
|
||||
@@ -22,38 +22,42 @@ func CommandApply() *cli.Command {
|
||||
Name: flagSubdomain,
|
||||
Usage: "Subdomain of the Nhost project to apply configuration to. Defaults to linked project",
|
||||
Required: true,
|
||||
EnvVars: []string{"NHOST_SUBDOMAIN"},
|
||||
Sources: cli.EnvVars("NHOST_SUBDOMAIN"),
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagYes,
|
||||
Usage: "Skip confirmation",
|
||||
EnvVars: []string{"NHOST_YES"},
|
||||
Sources: cli.EnvVars("NHOST_YES"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func commandApply(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandApply(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
|
||||
proj, err := ce.GetAppInfo(ctx, cmd.String(flagSubdomain))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app info: %w", err)
|
||||
return cli.Exit(fmt.Sprintf("Failed to get app info: %v", err), 1)
|
||||
}
|
||||
|
||||
ce.Infoln("Validating configuration...")
|
||||
|
||||
cfg, _, err := ValidateRemote(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
ce,
|
||||
proj.GetSubdomain(),
|
||||
proj.GetID(),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
return Apply(cCtx.Context, ce, proj.ID, cfg, cCtx.Bool(flagYes))
|
||||
if err := Apply(ctx, ce, proj.ID, cfg, cmd.Bool(flagYes)); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Apply(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package config
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
import "github.com/urfave/cli/v3"
|
||||
|
||||
const flagSubdomain = "subdomain"
|
||||
|
||||
@@ -9,7 +9,7 @@ func Command() *cli.Command {
|
||||
Name: "config",
|
||||
Aliases: []string{},
|
||||
Usage: "Perform config operations",
|
||||
Subcommands: []*cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
CommandDefault(),
|
||||
CommandExample(),
|
||||
CommandApply(),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
@@ -8,7 +9,7 @@ import (
|
||||
"github.com/nhost/nhost/cli/project"
|
||||
"github.com/nhost/nhost/cli/project/env"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandDefault() *cli.Command {
|
||||
@@ -21,8 +22,8 @@ func CommandDefault() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandDefault(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandDefault(_ context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
if err := os.MkdirAll(ce.Path.NhostFolder(), 0o755); err != nil { //nolint:mnd
|
||||
return fmt.Errorf("failed to create nhost folder: %w", err)
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
"github.com/wI2L/jsondiff"
|
||||
)
|
||||
|
||||
@@ -31,13 +31,13 @@ func CommandEdit() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagSubdomain,
|
||||
Usage: "If specified, edit this subdomain's overlay, otherwise edit base configuation",
|
||||
EnvVars: []string{"NHOST_SUBDOMAIN"},
|
||||
Sources: cli.EnvVars("NHOST_SUBDOMAIN"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagEditor,
|
||||
Usage: "Editor to use",
|
||||
Value: "vim",
|
||||
EnvVars: []string{"EDITOR"},
|
||||
Sources: cli.EnvVars("EDITOR"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -139,11 +139,11 @@ func GenerateJSONPatch(origfilepath, newfilepath, dst string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func edit(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func edit(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
if cCtx.String(flagSubdomain) == "" {
|
||||
if err := EditFile(cCtx.Context, cCtx.String(flagEditor), ce.Path.NhostToml()); err != nil {
|
||||
if cmd.String(flagSubdomain) == "" {
|
||||
if err := EditFile(ctx, cmd.String(flagEditor), ce.Path.NhostToml()); err != nil {
|
||||
return fmt.Errorf("failed to edit config: %w", err)
|
||||
}
|
||||
|
||||
@@ -163,17 +163,17 @@ func edit(cCtx *cli.Context) error {
|
||||
tmpfileName := filepath.Join(tmpdir, "nhost.toml")
|
||||
|
||||
if err := CopyConfig[model.ConfigConfig](
|
||||
ce.Path.NhostToml(), tmpfileName, ce.Path.Overlay(cCtx.String(flagSubdomain)),
|
||||
ce.Path.NhostToml(), tmpfileName, ce.Path.Overlay(cmd.String(flagSubdomain)),
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to copy config: %w", err)
|
||||
}
|
||||
|
||||
if err := EditFile(cCtx.Context, cCtx.String(flagEditor), tmpfileName); err != nil {
|
||||
if err := EditFile(ctx, cmd.String(flagEditor), tmpfileName); err != nil {
|
||||
return fmt.Errorf("failed to edit config: %w", err)
|
||||
}
|
||||
|
||||
if err := GenerateJSONPatch(
|
||||
ce.Path.NhostToml(), tmpfileName, ce.Path.Overlay(cCtx.String(flagSubdomain)),
|
||||
ce.Path.NhostToml(), tmpfileName, ce.Path.Overlay(cmd.String(flagSubdomain)),
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to generate json patch: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/be/services/mimir/schema"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandExample() *cli.Command {
|
||||
@@ -22,8 +23,8 @@ func CommandExample() *cli.Command {
|
||||
|
||||
func ptr[T any](v T) *T { return &v }
|
||||
|
||||
func commandExample(cCtx *cli.Context) error { //nolint:funlen,maintidx
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandExample(_ context.Context, cmd *cli.Command) error { //nolint:funlen,maintidx
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
//nolint:mnd
|
||||
cfg := model.ConfigConfig{
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/nhost/nhost/cli/project/env"
|
||||
"github.com/nhost/nhost/cli/system"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -36,21 +36,21 @@ func CommandPull() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagSubdomain,
|
||||
Usage: "Pull this subdomain's configuration. Defaults to linked project",
|
||||
EnvVars: []string{"NHOST_SUBDOMAIN"},
|
||||
Sources: cli.EnvVars("NHOST_SUBDOMAIN"),
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagYes,
|
||||
Usage: "Skip confirmation",
|
||||
EnvVars: []string{"NHOST_YES"},
|
||||
Sources: cli.EnvVars("NHOST_YES"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func commandPull(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandPull(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
skipConfirmation := cCtx.Bool(flagYes)
|
||||
skipConfirmation := cmd.Bool(flagYes)
|
||||
|
||||
if !skipConfirmation {
|
||||
if err := verifyFile(ce, ce.Path.NhostToml()); err != nil {
|
||||
@@ -66,12 +66,12 @@ func commandPull(cCtx *cli.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
|
||||
proj, err := ce.GetAppInfo(ctx, cmd.String(flagSubdomain))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app info: %w", err)
|
||||
}
|
||||
|
||||
_, err = Pull(cCtx.Context, ce, proj, writeSecrets)
|
||||
_, err = Pull(ctx, ce, proj, writeSecrets)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/project/env"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandShow() *cli.Command {
|
||||
@@ -21,14 +22,14 @@ func CommandShow() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagSubdomain,
|
||||
Usage: "Show this subdomain's rendered configuration. Defaults to base configuration",
|
||||
EnvVars: []string{"NHOST_SUBDOMAIN"},
|
||||
Sources: cli.EnvVars("NHOST_SUBDOMAIN"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func commandShow(c *cli.Context) error {
|
||||
ce := clienv.FromCLI(c)
|
||||
func commandShow(_ context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
var secrets model.Secrets
|
||||
if err := clienv.UnmarshalFile(ce.Path.Secrets(), &secrets, env.Unmarshal); err != nil {
|
||||
@@ -38,7 +39,7 @@ func commandShow(c *cli.Context) error {
|
||||
)
|
||||
}
|
||||
|
||||
cfg, err := Validate(ce, c.String(flagSubdomain), secrets)
|
||||
cfg, err := Validate(ce, cmd.String(flagSubdomain), secrets)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/project/env"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
jsonpatch "gopkg.in/evanphx/json-patch.v5"
|
||||
)
|
||||
|
||||
@@ -27,24 +27,24 @@ func CommandValidate() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagSubdomain,
|
||||
Usage: "Validate this subdomain's configuration. Defaults to linked project",
|
||||
EnvVars: []string{"NHOST_SUBDOMAIN"},
|
||||
Sources: cli.EnvVars("NHOST_SUBDOMAIN"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func commandValidate(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandValidate(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
subdomain := cCtx.String(flagSubdomain)
|
||||
subdomain := cmd.String(flagSubdomain)
|
||||
if subdomain != "" && subdomain != "local" {
|
||||
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
|
||||
proj, err := ce.GetAppInfo(ctx, cmd.String(flagSubdomain))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app info: %w", err)
|
||||
}
|
||||
|
||||
_, _, err = ValidateRemote(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
ce,
|
||||
proj.GetSubdomain(),
|
||||
proj.GetID(),
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/google/uuid"
|
||||
"github.com/nhost/be/services/mimir/graph"
|
||||
cors "github.com/rs/cors/wrapper/gin"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -48,27 +48,27 @@ func Command() *cli.Command {
|
||||
Name: enablePlaygroundFlag,
|
||||
Usage: "enable graphql playground (under /v1)",
|
||||
Category: "server",
|
||||
EnvVars: []string{"ENABLE_PLAYGROUND"},
|
||||
Sources: cli.EnvVars("ENABLE_PLAYGROUND"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint: exhaustruct
|
||||
Name: storageLocalConfigPath,
|
||||
Usage: "Path to the local mimir config file",
|
||||
Value: "/tmp/root/nhost/nhost.toml",
|
||||
Category: "plugins",
|
||||
EnvVars: []string{"STORAGE_LOCAL_CONFIG_PATH"},
|
||||
Sources: cli.EnvVars("STORAGE_LOCAL_CONFIG_PATH"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint: exhaustruct
|
||||
Name: storageLocalSecretsPath,
|
||||
Usage: "Path to the local mimir secrets file",
|
||||
Value: "/tmp/root/.secrets",
|
||||
Category: "plugins",
|
||||
EnvVars: []string{"STORAGE_LOCAL_SECRETS_PATH"},
|
||||
Sources: cli.EnvVars("STORAGE_LOCAL_SECRETS_PATH"),
|
||||
},
|
||||
&cli.StringSliceFlag{ //nolint: exhaustruct
|
||||
Name: storageLocalRunServicesPath,
|
||||
Usage: "Path to the local mimir run services files",
|
||||
Category: "plugins",
|
||||
EnvVars: []string{"STORAGE_LOCAL_RUN_SERVICES_PATH"},
|
||||
Sources: cli.EnvVars("STORAGE_LOCAL_RUN_SERVICES_PATH"),
|
||||
},
|
||||
},
|
||||
Action: serve,
|
||||
@@ -103,14 +103,14 @@ func runServicesFiles(runServices ...string) map[string]string {
|
||||
return m
|
||||
}
|
||||
|
||||
func serve(cCtx *cli.Context) error {
|
||||
logger := getLogger(cCtx.Bool(debugFlag), cCtx.Bool(logFormatJSONFlag))
|
||||
logger.Info(cCtx.App.Name + " v" + cCtx.App.Version)
|
||||
logFlags(logger, cCtx)
|
||||
func serve(_ context.Context, cmd *cli.Command) error {
|
||||
logger := getLogger(cmd.Bool(debugFlag), cmd.Bool(logFormatJSONFlag))
|
||||
logger.Info(cmd.Root().Name + " v" + cmd.Root().Version)
|
||||
logFlags(logger, cmd)
|
||||
|
||||
configFile := cCtx.String(storageLocalConfigPath)
|
||||
secretsFile := cCtx.String(storageLocalSecretsPath)
|
||||
runServices := runServicesFiles(cCtx.StringSlice(storageLocalRunServicesPath)...)
|
||||
configFile := cmd.String(storageLocalConfigPath)
|
||||
secretsFile := cmd.String(storageLocalSecretsPath)
|
||||
runServices := runServicesFiles(cmd.StringSlice(storageLocalRunServicesPath)...)
|
||||
|
||||
st := NewLocal(configFile, secretsFile, runServices)
|
||||
|
||||
@@ -131,13 +131,13 @@ func serve(cCtx *cli.Context) error {
|
||||
resolver,
|
||||
dummyMiddleware,
|
||||
dummyMiddleware2,
|
||||
cCtx.Bool(enablePlaygroundFlag),
|
||||
cCtx.App.Version,
|
||||
cmd.Bool(enablePlaygroundFlag),
|
||||
cmd.Root().Version,
|
||||
[]graphql.FieldMiddleware{},
|
||||
gin.Recovery(),
|
||||
cors.Default(),
|
||||
)
|
||||
if err := r.Run(cCtx.String(bindFlag)); err != nil {
|
||||
if err := r.Run(cmd.String(bindFlag)); err != nil {
|
||||
return fmt.Errorf("failed to run gin: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func getLogger(debug bool, formatJSON bool) *logrus.Logger {
|
||||
@@ -29,15 +29,15 @@ func getLogger(debug bool, formatJSON bool) *logrus.Logger {
|
||||
return logger
|
||||
}
|
||||
|
||||
func logFlags(logger logrus.FieldLogger, cCtx *cli.Context) {
|
||||
func logFlags(logger logrus.FieldLogger, cmd *cli.Command) {
|
||||
fields := logrus.Fields{}
|
||||
|
||||
for _, flag := range cCtx.App.Flags {
|
||||
for _, flag := range cmd.Root().Flags {
|
||||
name := flag.Names()[0]
|
||||
fields[name] = cCtx.Generic(name)
|
||||
fields[name] = cmd.Value(name)
|
||||
}
|
||||
|
||||
for _, flag := range cCtx.Command.Flags {
|
||||
for _, flag := range cmd.Flags {
|
||||
name := flag.Names()[0]
|
||||
if strings.Contains(name, "pass") ||
|
||||
strings.Contains(name, "token") ||
|
||||
@@ -47,7 +47,7 @@ func logFlags(logger logrus.FieldLogger, cCtx *cli.Context) {
|
||||
continue
|
||||
}
|
||||
|
||||
fields[name] = cCtx.Generic(name)
|
||||
fields[name] = cmd.Value(name)
|
||||
}
|
||||
|
||||
logger.WithFields(fields).Info("started with settings")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package deployments
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
import "github.com/urfave/cli/v3"
|
||||
|
||||
const flagSubdomain = "subdomain"
|
||||
|
||||
@@ -9,7 +9,7 @@ func commonFlags() []cli.Flag {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagSubdomain,
|
||||
Usage: "Project's subdomain to operate on, defaults to linked project",
|
||||
EnvVars: []string{"NHOST_SUBDOMAIN"},
|
||||
Sources: cli.EnvVars("NHOST_SUBDOMAIN"),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func Command() *cli.Command {
|
||||
Name: "deployments",
|
||||
Aliases: []string{},
|
||||
Usage: "Manage deployments",
|
||||
Subcommands: []*cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
CommandList(),
|
||||
CommandLogs(),
|
||||
CommandNew(),
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package deployments
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/nhostclient/graphql"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandList() *cli.Command {
|
||||
@@ -77,21 +78,21 @@ func printDeployments(ce *clienv.CliEnv, deployments []*graphql.ListDeployments_
|
||||
ce.Println("%s", clienv.Table(id, date, duration, status, user, ref, message))
|
||||
}
|
||||
|
||||
func commandList(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandList(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
|
||||
proj, err := ce.GetAppInfo(ctx, cmd.String(flagSubdomain))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app info: %w", err)
|
||||
}
|
||||
|
||||
cl, err := ce.GetNhostClient(cCtx.Context)
|
||||
cl, err := ce.GetNhostClient(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nhost client: %w", err)
|
||||
}
|
||||
|
||||
deployments, err := cl.ListDeployments(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
proj.ID,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/nhostclient"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -101,28 +101,28 @@ func showLogsFollow(
|
||||
}
|
||||
}
|
||||
|
||||
func commandLogs(cCtx *cli.Context) error {
|
||||
deploymentID := cCtx.Args().First()
|
||||
func commandLogs(ctx context.Context, cmd *cli.Command) error {
|
||||
deploymentID := cmd.Args().First()
|
||||
if deploymentID == "" {
|
||||
return errors.New("deployment_id is required") //nolint:err113
|
||||
}
|
||||
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
cl, err := ce.GetNhostClient(cCtx.Context)
|
||||
cl, err := ce.GetNhostClient(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nhost client: %w", err)
|
||||
}
|
||||
|
||||
if cCtx.Bool(flagFollow) {
|
||||
ctx, cancel := context.WithTimeout(cCtx.Context, cCtx.Duration(flagTimeout))
|
||||
if cmd.Bool(flagFollow) {
|
||||
ctxWithTimeout, cancel := context.WithTimeout(ctx, cmd.Duration(flagTimeout))
|
||||
defer cancel()
|
||||
|
||||
if _, err := showLogsFollow(ctx, ce, cl, deploymentID); err != nil {
|
||||
if _, err := showLogsFollow(ctxWithTimeout, ce, cl, deploymentID); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := showLogsSimple(cCtx.Context, ce, cl, deploymentID); err != nil {
|
||||
if err := showLogsSimple(ctx, ce, cl, deploymentID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/nhostclient/graphql"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -40,7 +40,7 @@ func CommandNew() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagRef,
|
||||
Usage: "Git reference",
|
||||
EnvVars: []string{"GITHUB_SHA"},
|
||||
Sources: cli.EnvVars("GITHUB_SHA"),
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
@@ -51,7 +51,7 @@ func CommandNew() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagUser,
|
||||
Usage: "Commit user name",
|
||||
EnvVars: []string{"GITHUB_ACTOR"},
|
||||
Sources: cli.EnvVars("GITHUB_ACTOR"),
|
||||
Required: true,
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
@@ -67,28 +67,28 @@ func ptr[i any](v i) *i {
|
||||
return &v
|
||||
}
|
||||
|
||||
func commandNew(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandNew(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
cl, err := ce.GetNhostClient(cCtx.Context)
|
||||
cl, err := ce.GetNhostClient(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nhost client: %w", err)
|
||||
}
|
||||
|
||||
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
|
||||
proj, err := ce.GetAppInfo(ctx, cmd.String(flagSubdomain))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app info: %w", err)
|
||||
}
|
||||
|
||||
resp, err := cl.InsertDeployment(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
graphql.DeploymentsInsertInput{
|
||||
App: nil,
|
||||
AppID: ptr(proj.ID),
|
||||
CommitMessage: ptr(cCtx.String(flagMessage)),
|
||||
CommitSha: ptr(cCtx.String(flagRef)),
|
||||
CommitUserAvatarURL: ptr(cCtx.String(flagUserAvatarURL)),
|
||||
CommitUserName: ptr(cCtx.String(flagUser)),
|
||||
CommitMessage: ptr(cmd.String(flagMessage)),
|
||||
CommitSha: ptr(cmd.String(flagRef)),
|
||||
CommitUserAvatarURL: ptr(cmd.String(flagUserAvatarURL)),
|
||||
CommitUserName: ptr(cmd.String(flagUser)),
|
||||
DeploymentStatus: ptr("SCHEDULED"),
|
||||
},
|
||||
)
|
||||
@@ -98,13 +98,13 @@ func commandNew(cCtx *cli.Context) error {
|
||||
|
||||
ce.Println("Deployment created: %s", resp.InsertDeployment.ID)
|
||||
|
||||
if cCtx.Bool(flagFollow) {
|
||||
if cmd.Bool(flagFollow) {
|
||||
ce.Println("")
|
||||
|
||||
ctx, cancel := context.WithTimeout(cCtx.Context, cCtx.Duration(flagTimeout))
|
||||
ctxWithTimeout, cancel := context.WithTimeout(ctx, cmd.Duration(flagTimeout))
|
||||
defer cancel()
|
||||
|
||||
status, err := showLogsFollow(ctx, ce, cl, resp.InsertDeployment.ID)
|
||||
status, err := showLogsFollow(ctxWithTimeout, ce, cl, resp.InsertDeployment.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error streaming logs: %w", err)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/nhost/nhost/cli/cmd/software"
|
||||
"github.com/nhost/nhost/cli/dockercompose"
|
||||
"github.com/nhost/nhost/cli/nhostclient/graphql"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -34,19 +34,19 @@ func CommandCloud() *cli.Command {
|
||||
Name: flagHTTPPort,
|
||||
Usage: "HTTP port to listen on",
|
||||
Value: defaultHTTPPort,
|
||||
EnvVars: []string{"NHOST_HTTP_PORT"},
|
||||
Sources: cli.EnvVars("NHOST_HTTP_PORT"),
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagDisableTLS,
|
||||
Usage: "Disable TLS",
|
||||
Value: false,
|
||||
EnvVars: []string{"NHOST_DISABLE_TLS"},
|
||||
Sources: cli.EnvVars("NHOST_DISABLE_TLS"),
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagApplySeeds,
|
||||
Usage: "Apply seeds. If the .nhost folder does not exist, seeds will be applied regardless of this flag",
|
||||
Value: false,
|
||||
EnvVars: []string{"NHOST_APPLY_SEEDS"},
|
||||
Sources: cli.EnvVars("NHOST_APPLY_SEEDS"),
|
||||
},
|
||||
&cli.UintFlag{ //nolint:exhaustruct
|
||||
Name: flagsHasuraConsolePort,
|
||||
@@ -56,42 +56,42 @@ func CommandCloud() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagDashboardVersion,
|
||||
Usage: "Dashboard version to use",
|
||||
Value: "nhost/dashboard:2.33.0",
|
||||
EnvVars: []string{"NHOST_DASHBOARD_VERSION"},
|
||||
Value: "nhost/dashboard:2.38.0",
|
||||
Sources: cli.EnvVars("NHOST_DASHBOARD_VERSION"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagConfigserverImage,
|
||||
Hidden: true,
|
||||
Value: "",
|
||||
EnvVars: []string{"NHOST_CONFIGSERVER_IMAGE"},
|
||||
Sources: cli.EnvVars("NHOST_CONFIGSERVER_IMAGE"),
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagDownOnError,
|
||||
Usage: "Skip confirmation",
|
||||
EnvVars: []string{"NHOST_YES"},
|
||||
Sources: cli.EnvVars("NHOST_YES"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagCACertificates,
|
||||
Usage: "Mounts and everrides path to CA certificates in the containers",
|
||||
EnvVars: []string{"NHOST_CA_CERTIFICATES"},
|
||||
Sources: cli.EnvVars("NHOST_CA_CERTIFICATES"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagSubdomain,
|
||||
Usage: "Project's subdomain to operate on, defaults to linked project",
|
||||
EnvVars: []string{"NHOST_SUBDOMAIN"},
|
||||
Sources: cli.EnvVars("NHOST_SUBDOMAIN"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagPostgresURL,
|
||||
Usage: "Postgres URL",
|
||||
Required: true,
|
||||
EnvVars: []string{"NHOST_POSTGRES_URL"},
|
||||
Sources: cli.EnvVars("NHOST_POSTGRES_URL"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func commandCloud(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandCloud(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
if !clienv.PathExists(ce.Path.NhostToml()) {
|
||||
return errors.New( //nolint:err113
|
||||
@@ -105,38 +105,38 @@ func commandCloud(cCtx *cli.Context) error {
|
||||
)
|
||||
}
|
||||
|
||||
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
|
||||
proj, err := ce.GetAppInfo(ctx, cmd.String(flagSubdomain))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app info: %w", err)
|
||||
}
|
||||
|
||||
configserverImage := cCtx.String(flagConfigserverImage)
|
||||
configserverImage := cmd.String(flagConfigserverImage)
|
||||
if configserverImage == "" {
|
||||
configserverImage = "nhost/cli:" + cCtx.App.Version
|
||||
configserverImage = "nhost/cli:" + cmd.Root().Version
|
||||
}
|
||||
|
||||
applySeeds := cCtx.Bool(flagApplySeeds)
|
||||
applySeeds := cmd.Bool(flagApplySeeds)
|
||||
|
||||
return Cloud(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
ce,
|
||||
cCtx.App.Version,
|
||||
cCtx.Uint(flagHTTPPort),
|
||||
!cCtx.Bool(flagDisableTLS),
|
||||
cmd.Root().Version,
|
||||
cmd.Uint(flagHTTPPort),
|
||||
!cmd.Bool(flagDisableTLS),
|
||||
applySeeds,
|
||||
dockercompose.ExposePorts{
|
||||
Auth: cCtx.Uint(flagAuthPort),
|
||||
Storage: cCtx.Uint(flagStoragePort),
|
||||
Graphql: cCtx.Uint(flagsHasuraPort),
|
||||
Console: cCtx.Uint(flagsHasuraConsolePort),
|
||||
Functions: cCtx.Uint(flagsFunctionsPort),
|
||||
Auth: cmd.Uint(flagAuthPort),
|
||||
Storage: cmd.Uint(flagStoragePort),
|
||||
Graphql: cmd.Uint(flagsHasuraPort),
|
||||
Console: cmd.Uint(flagsHasuraConsolePort),
|
||||
Functions: cmd.Uint(flagsFunctionsPort),
|
||||
},
|
||||
cCtx.String(flagDashboardVersion),
|
||||
cmd.String(flagDashboardVersion),
|
||||
configserverImage,
|
||||
cCtx.String(flagCACertificates),
|
||||
cCtx.Bool(flagDownOnError),
|
||||
cmd.String(flagCACertificates),
|
||||
cmd.Bool(flagDownOnError),
|
||||
proj,
|
||||
cCtx.String(flagPostgresURL),
|
||||
cmd.String(flagPostgresURL),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package dev
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/dockercompose"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandCompose() *cli.Command {
|
||||
@@ -17,9 +19,9 @@ func CommandCompose() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandCompose(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandCompose(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
dc := dockercompose.New(ce.Path.WorkingDir(), ce.Path.DockerCompose(), ce.ProjectName())
|
||||
|
||||
return dc.Wrapper(cCtx.Context, cCtx.Args().Slice()...) //nolint:wrapcheck
|
||||
return dc.Wrapper(ctx, cmd.Args().Slice()...) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package dev
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
import "github.com/urfave/cli/v3"
|
||||
|
||||
func Command() *cli.Command {
|
||||
return &cli.Command{ //nolint:exhaustruct
|
||||
Name: "dev",
|
||||
Aliases: []string{},
|
||||
Usage: "Operate local development environment",
|
||||
Subcommands: []*cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
CommandCompose(),
|
||||
CommandHasura(),
|
||||
},
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package dev
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/dockercompose"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,12 +28,12 @@ func CommandDown() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandDown(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandDown(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
dc := dockercompose.New(ce.Path.WorkingDir(), ce.Path.DockerCompose(), ce.ProjectName())
|
||||
|
||||
if err := dc.Stop(cCtx.Context, cCtx.Bool(flagVolumes)); err != nil {
|
||||
if err := dc.Stop(ctx, cmd.Bool(flagVolumes)); err != nil {
|
||||
ce.Warnln("failed to stop Nhost development environment: %s", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package dev
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/dockercompose"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandHasura() *cli.Command {
|
||||
@@ -21,8 +22,8 @@ func CommandHasura() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandHasura(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandHasura(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
cfg := &model.ConfigConfig{} //nolint:exhaustruct
|
||||
if err := clienv.UnmarshalFile(ce.Path.NhostToml(), cfg, toml.Unmarshal); err != nil {
|
||||
@@ -32,10 +33,10 @@ func commandHasura(cCtx *cli.Context) error {
|
||||
docker := dockercompose.NewDocker()
|
||||
|
||||
return docker.HasuraWrapper( //nolint:wrapcheck
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
ce.LocalSubdomain(),
|
||||
ce.Path.NhostFolder(),
|
||||
*cfg.Hasura.Version,
|
||||
cCtx.Args().Slice()...,
|
||||
cmd.Args().Slice()...,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package dev
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/dockercompose"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandLogs() *cli.Command {
|
||||
@@ -17,12 +19,12 @@ func CommandLogs() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandLogs(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandLogs(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
dc := dockercompose.New(ce.Path.WorkingDir(), ce.Path.DockerCompose(), ce.ProjectName())
|
||||
|
||||
if err := dc.Logs(cCtx.Context, cCtx.Args().Slice()...); err != nil {
|
||||
if err := dc.Logs(ctx, cmd.Args().Slice()...); err != nil {
|
||||
ce.Warnln("%s", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ import (
|
||||
"github.com/nhost/nhost/cli/cmd/software"
|
||||
"github.com/nhost/nhost/cli/dockercompose"
|
||||
"github.com/nhost/nhost/cli/project/env"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func deptr[T any](t *T) T { //nolint:ireturn
|
||||
@@ -63,25 +63,25 @@ func CommandUp() *cli.Command { //nolint:funlen
|
||||
Name: flagHTTPPort,
|
||||
Usage: "HTTP port to listen on",
|
||||
Value: defaultHTTPPort,
|
||||
EnvVars: []string{"NHOST_HTTP_PORT"},
|
||||
Sources: cli.EnvVars("NHOST_HTTP_PORT"),
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagDisableTLS,
|
||||
Usage: "Disable TLS",
|
||||
Value: false,
|
||||
EnvVars: []string{"NHOST_DISABLE_TLS"},
|
||||
Sources: cli.EnvVars("NHOST_DISABLE_TLS"),
|
||||
},
|
||||
&cli.UintFlag{ //nolint:exhaustruct
|
||||
Name: flagPostgresPort,
|
||||
Usage: "Postgres port to listen on",
|
||||
Value: defaultPostgresPort,
|
||||
EnvVars: []string{"NHOST_POSTGRES_PORT"},
|
||||
Sources: cli.EnvVars("NHOST_POSTGRES_PORT"),
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagApplySeeds,
|
||||
Usage: "Apply seeds. If the .nhost folder does not exist, seeds will be applied regardless of this flag",
|
||||
Value: false,
|
||||
EnvVars: []string{"NHOST_APPLY_SEEDS"},
|
||||
Sources: cli.EnvVars("NHOST_APPLY_SEEDS"),
|
||||
},
|
||||
&cli.UintFlag{ //nolint:exhaustruct
|
||||
Name: flagAuthPort,
|
||||
@@ -111,39 +111,39 @@ func CommandUp() *cli.Command { //nolint:funlen
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagDashboardVersion,
|
||||
Usage: "Dashboard version to use",
|
||||
Value: "nhost/dashboard:2.33.0",
|
||||
EnvVars: []string{"NHOST_DASHBOARD_VERSION"},
|
||||
Value: "nhost/dashboard:2.38.0",
|
||||
Sources: cli.EnvVars("NHOST_DASHBOARD_VERSION"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagConfigserverImage,
|
||||
Hidden: true,
|
||||
Value: "",
|
||||
EnvVars: []string{"NHOST_CONFIGSERVER_IMAGE"},
|
||||
Sources: cli.EnvVars("NHOST_CONFIGSERVER_IMAGE"),
|
||||
},
|
||||
&cli.StringSliceFlag{ //nolint:exhaustruct
|
||||
Name: flagRunService,
|
||||
Usage: "Run service to add to the development environment. Can be passed multiple times. Comma-separated values are also accepted. Format: /path/to/run-service.toml[:overlay_name]", //nolint:lll
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE"),
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagDownOnError,
|
||||
Usage: "Skip confirmation",
|
||||
EnvVars: []string{"NHOST_YES"},
|
||||
Sources: cli.EnvVars("NHOST_YES"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagCACertificates,
|
||||
Usage: "Mounts and everrides path to CA certificates in the containers",
|
||||
EnvVars: []string{"NHOST_CA_CERTIFICATES"},
|
||||
Sources: cli.EnvVars("NHOST_CA_CERTIFICATES"),
|
||||
},
|
||||
},
|
||||
Subcommands: []*cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
CommandCloud(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func commandUp(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandUp(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
// projname to be root directory
|
||||
|
||||
@@ -159,33 +159,33 @@ func commandUp(cCtx *cli.Context) error {
|
||||
)
|
||||
}
|
||||
|
||||
configserverImage := cCtx.String(flagConfigserverImage)
|
||||
configserverImage := cmd.String(flagConfigserverImage)
|
||||
if configserverImage == "" {
|
||||
configserverImage = "nhost/cli:" + cCtx.App.Version
|
||||
configserverImage = "nhost/cli:" + cmd.Root().Version
|
||||
}
|
||||
|
||||
applySeeds := cCtx.Bool(flagApplySeeds) || !clienv.PathExists(ce.Path.DotNhostFolder())
|
||||
applySeeds := cmd.Bool(flagApplySeeds) || !clienv.PathExists(ce.Path.DotNhostFolder())
|
||||
|
||||
return Up(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
ce,
|
||||
cCtx.App.Version,
|
||||
cCtx.Uint(flagHTTPPort),
|
||||
!cCtx.Bool(flagDisableTLS),
|
||||
cCtx.Uint(flagPostgresPort),
|
||||
cmd.Root().Version,
|
||||
cmd.Uint(flagHTTPPort),
|
||||
!cmd.Bool(flagDisableTLS),
|
||||
cmd.Uint(flagPostgresPort),
|
||||
applySeeds,
|
||||
dockercompose.ExposePorts{
|
||||
Auth: cCtx.Uint(flagAuthPort),
|
||||
Storage: cCtx.Uint(flagStoragePort),
|
||||
Graphql: cCtx.Uint(flagsHasuraPort),
|
||||
Console: cCtx.Uint(flagsHasuraConsolePort),
|
||||
Functions: cCtx.Uint(flagsFunctionsPort),
|
||||
Auth: cmd.Uint(flagAuthPort),
|
||||
Storage: cmd.Uint(flagStoragePort),
|
||||
Graphql: cmd.Uint(flagsHasuraPort),
|
||||
Console: cmd.Uint(flagsHasuraConsolePort),
|
||||
Functions: cmd.Uint(flagsFunctionsPort),
|
||||
},
|
||||
cCtx.String(flagDashboardVersion),
|
||||
cmd.String(flagDashboardVersion),
|
||||
configserverImage,
|
||||
cCtx.String(flagCACertificates),
|
||||
cCtx.StringSlice(flagRunService),
|
||||
cCtx.Bool(flagDownOnError),
|
||||
cmd.String(flagCACertificates),
|
||||
cmd.StringSlice(flagRunService),
|
||||
cmd.Bool(flagDownOnError),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"os/exec"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,13 +35,13 @@ func CommandConfigure() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagDockerConfig,
|
||||
Usage: "Path to docker config file",
|
||||
EnvVars: []string{"DOCKER_CONFIG"},
|
||||
Sources: cli.EnvVars("DOCKER_CONFIG"),
|
||||
Value: home + "/.docker/config.json",
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagNoInteractive,
|
||||
Usage: "Do not prompt for confirmation",
|
||||
EnvVars: []string{"NO_INTERACTIVE"},
|
||||
Sources: cli.EnvVars("NO_INTERACTIVE"),
|
||||
Value: false,
|
||||
},
|
||||
},
|
||||
@@ -140,21 +140,21 @@ func configureDocker(dockerConfig string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func actionConfigure(c *cli.Context) error {
|
||||
ce := clienv.FromCLI(c)
|
||||
func actionConfigure(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
if err := writeScript(c.Context, ce); err != nil {
|
||||
if err := writeScript(ctx, ce); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.Bool(flagNoInteractive) {
|
||||
return configureDocker(c.String(flagDockerConfig))
|
||||
if cmd.Bool(flagNoInteractive) {
|
||||
return configureDocker(cmd.String(flagDockerConfig))
|
||||
}
|
||||
|
||||
//nolint:lll
|
||||
ce.PromptMessage(
|
||||
"I am about to configure docker to authenticate with Nhost's registry. This will modify your docker config file on %s. Should I continue? [y/N] ",
|
||||
c.String(flagDockerConfig),
|
||||
cmd.String(flagDockerConfig),
|
||||
)
|
||||
|
||||
v, err := ce.PromptInput(false)
|
||||
@@ -163,7 +163,7 @@ func actionConfigure(c *cli.Context) error {
|
||||
}
|
||||
|
||||
if v == "y" || v == "Y" {
|
||||
return configureDocker(c.String(flagDockerConfig))
|
||||
return configureDocker(cmd.String(flagDockerConfig))
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package dockercredentials
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
import "github.com/urfave/cli/v3"
|
||||
|
||||
func Command() *cli.Command {
|
||||
return &cli.Command{ //nolint:exhaustruct
|
||||
Name: "docker-credentials",
|
||||
Aliases: []string{},
|
||||
Usage: "Perform docker-credentials operations",
|
||||
Subcommands: []*cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
CommandGet(),
|
||||
CommandErase(),
|
||||
CommandStore(),
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package dockercredentials
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
"context"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandErase() *cli.Command {
|
||||
@@ -14,7 +16,7 @@ func CommandErase() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func actionErase(c *cli.Context) error {
|
||||
_, _ = c.App.Writer.Write([]byte("Please, use the nhost CLI to logout\n"))
|
||||
func actionErase(_ context.Context, cmd *cli.Command) error {
|
||||
_, _ = cmd.Root().Writer.Write([]byte("Please, use the nhost CLI to logout\n"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -26,14 +26,14 @@ func CommandGet() *cli.Command {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagAuthURL,
|
||||
Usage: "Nhost auth URL",
|
||||
EnvVars: []string{"NHOST_CLI_AUTH_URL"},
|
||||
Sources: cli.EnvVars("NHOST_CLI_AUTH_URL"),
|
||||
Value: "https://otsispdzcwxyqzbfntmj.auth.eu-central-1.nhost.run/v1",
|
||||
Hidden: true,
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagGraphqlURL,
|
||||
Usage: "Nhost GraphQL URL",
|
||||
EnvVars: []string{"NHOST_CLI_GRAPHQL_URL"},
|
||||
Sources: cli.EnvVars("NHOST_CLI_GRAPHQL_URL"),
|
||||
Value: "https://otsispdzcwxyqzbfntmj.graphql.eu-central-1.nhost.run/v1",
|
||||
Hidden: true,
|
||||
},
|
||||
@@ -69,15 +69,15 @@ type response struct {
|
||||
Secret string `json:"Secret"`
|
||||
}
|
||||
|
||||
func actionGet(c *cli.Context) error {
|
||||
scanner := bufio.NewScanner(c.App.Reader)
|
||||
func actionGet(ctx context.Context, cmd *cli.Command) error {
|
||||
scanner := bufio.NewScanner(cmd.Root().Reader)
|
||||
|
||||
var input string
|
||||
for scanner.Scan() {
|
||||
input += scanner.Text()
|
||||
}
|
||||
|
||||
token, err := getToken(c.Context, c.String(flagAuthURL), c.String(flagGraphqlURL))
|
||||
token, err := getToken(ctx, cmd.String(flagAuthURL), cmd.String(flagGraphqlURL))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -91,7 +91,7 @@ func actionGet(c *cli.Context) error {
|
||||
return fmt.Errorf("failed to marshal response: %w", err)
|
||||
}
|
||||
|
||||
if _, err = c.App.Writer.Write(b); err != nil {
|
||||
if _, err = cmd.Root().Writer.Write(b); err != nil {
|
||||
return fmt.Errorf("failed to write response: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package dockercredentials
|
||||
|
||||
import (
|
||||
"github.com/urfave/cli/v2"
|
||||
"context"
|
||||
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandStore() *cli.Command {
|
||||
@@ -14,7 +16,7 @@ func CommandStore() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func actionStore(c *cli.Context) error {
|
||||
_, _ = c.App.Writer.Write([]byte("Please, use the nhost CLI to login\n"))
|
||||
func actionStore(_ context.Context, cmd *cli.Command) error {
|
||||
_, _ = cmd.Root().Writer.Write([]byte("Please, use the nhost CLI to login\n"))
|
||||
return nil
|
||||
}
|
||||
|
||||
92
cli/cmd/mcp/config/config.go
Normal file
92
cli/cmd/mcp/config/config.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/nhost/nhost/cli/mcp/config"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
flagConfirm = "confirm"
|
||||
)
|
||||
|
||||
func Command() *cli.Command {
|
||||
return &cli.Command{ //nolint:exhaustruct
|
||||
Name: "config",
|
||||
Usage: "Generate and save configuration file",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagConfirm,
|
||||
Usage: "Skip confirmation prompt",
|
||||
Value: false,
|
||||
Sources: cli.EnvVars("CONFIRM"),
|
||||
},
|
||||
},
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "dump",
|
||||
Usage: "Dump the configuration to stdout for verification",
|
||||
Flags: []cli.Flag{},
|
||||
Action: actionDump,
|
||||
},
|
||||
},
|
||||
Action: action,
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:forbidigo
|
||||
func action(_ context.Context, cmd *cli.Command) error {
|
||||
cfg, err := config.RunWizard()
|
||||
if err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to run wizard: %s", err), 1)
|
||||
}
|
||||
|
||||
tomlData, err := toml.Marshal(cfg)
|
||||
if err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to marshal config: %s", err), 1)
|
||||
}
|
||||
|
||||
fmt.Println("Configuration Preview:")
|
||||
fmt.Println("---------------------")
|
||||
fmt.Println(string(tomlData))
|
||||
fmt.Println()
|
||||
|
||||
filePath := config.GetConfigPath(cmd)
|
||||
fmt.Printf("Save configuration to %s?\n", filePath)
|
||||
fmt.Print("Proceed? (y/N): ")
|
||||
|
||||
var confirm string
|
||||
if _, err := fmt.Scanln(&confirm); err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to read input: %s", err), 1)
|
||||
}
|
||||
|
||||
if confirm != "y" && confirm != "Y" {
|
||||
fmt.Println("Operation cancelled.")
|
||||
return nil
|
||||
}
|
||||
|
||||
dir := filepath.Dir(filePath)
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil { //nolint:mnd
|
||||
return fmt.Errorf("failed to create config directory: %w", err)
|
||||
}
|
||||
|
||||
data, err := toml.Marshal(cfg)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal config: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filePath, data, 0o600); err != nil { //nolint:mnd
|
||||
return fmt.Errorf("failed to write config file: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("\nConfiguration saved successfully!")
|
||||
fmt.Println("Note: Review the documentation for additional configuration options,")
|
||||
fmt.Println(" especially for fine-tuning LLM access permissions.")
|
||||
|
||||
return nil
|
||||
}
|
||||
35
cli/cmd/mcp/config/dump.go
Normal file
35
cli/cmd/mcp/config/dump.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/nhost/cli/mcp/config"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func actionDump(_ context.Context, cmd *cli.Command) error {
|
||||
configPath := config.GetConfigPath(cmd)
|
||||
if configPath == "" {
|
||||
return cli.Exit("config file path is required", 1)
|
||||
}
|
||||
|
||||
cfg, err := config.Load(configPath)
|
||||
if err != nil {
|
||||
fmt.Println("Please, run `mcp-nhost config` to configure the service.") //nolint:forbidigo
|
||||
return cli.Exit("failed to load config file "+err.Error(), 1)
|
||||
}
|
||||
|
||||
b, err := toml.Marshal(cfg)
|
||||
if err != nil {
|
||||
return cli.Exit("failed to marshal config file "+err.Error(), 1)
|
||||
}
|
||||
|
||||
fmt.Println("Configuration Preview:") //nolint:forbidigo
|
||||
fmt.Println("---------------------") //nolint:forbidigo
|
||||
fmt.Println(string(b)) //nolint:forbidigo
|
||||
fmt.Println() //nolint:forbidigo
|
||||
|
||||
return nil
|
||||
}
|
||||
117
cli/cmd/mcp/gen/gen.go
Normal file
117
cli/cmd/mcp/gen/gen.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package gen
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/nhost/cli/mcp/graphql"
|
||||
"github.com/nhost/nhost/cli/mcp/nhost/auth"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
flagNhostAuthURL = "nhost-auth-url"
|
||||
flagNhostGraphqlURL = "nhost-graphql-url"
|
||||
flagNhostPAT = "nhost-pat"
|
||||
flagWithMutations = "with-mutations"
|
||||
)
|
||||
|
||||
func Command() *cli.Command {
|
||||
return &cli.Command{ //nolint:exhaustruct
|
||||
Name: "gen",
|
||||
Usage: "Generate GraphQL schema for Nhost Cloud",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagNhostAuthURL,
|
||||
Usage: "Nhost auth URL",
|
||||
Hidden: true,
|
||||
Value: "https://otsispdzcwxyqzbfntmj.auth.eu-central-1.nhost.run/v1",
|
||||
Sources: cli.EnvVars("NHOST_AUTH_URL"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagNhostGraphqlURL,
|
||||
Usage: "Nhost GraphQL URL",
|
||||
Hidden: true,
|
||||
Value: "https://otsispdzcwxyqzbfntmj.graphql.eu-central-1.nhost.run/v1",
|
||||
Sources: cli.EnvVars("NHOST_GRAPHQL_URL"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagNhostPAT,
|
||||
Usage: "Personal Access Token",
|
||||
Required: true,
|
||||
Sources: cli.EnvVars("NHOST_PAT"),
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagWithMutations,
|
||||
Usage: "Include mutations in the generated schema",
|
||||
Value: false,
|
||||
Sources: cli.EnvVars("WITH_MUTATIONS"),
|
||||
},
|
||||
},
|
||||
Action: action,
|
||||
}
|
||||
}
|
||||
|
||||
func action(ctx context.Context, cmd *cli.Command) error {
|
||||
interceptor, err := auth.WithPAT(
|
||||
cmd.String(flagNhostAuthURL), cmd.String(flagNhostPAT),
|
||||
)
|
||||
if err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
var introspection graphql.ResponseIntrospection
|
||||
if err := graphql.Query(
|
||||
ctx,
|
||||
cmd.String(flagNhostGraphqlURL),
|
||||
graphql.IntrospectionQuery,
|
||||
nil,
|
||||
&introspection,
|
||||
nil,
|
||||
nil,
|
||||
interceptor,
|
||||
); err != nil {
|
||||
return cli.Exit(err.Error(), 1)
|
||||
}
|
||||
|
||||
filter := graphql.Filter{
|
||||
AllowQueries: []graphql.Queries{
|
||||
{
|
||||
Name: "organizations",
|
||||
DisableNesting: true,
|
||||
},
|
||||
{
|
||||
Name: "organization",
|
||||
DisableNesting: true,
|
||||
},
|
||||
{
|
||||
Name: "app",
|
||||
DisableNesting: true,
|
||||
},
|
||||
{
|
||||
Name: "apps",
|
||||
DisableNesting: true,
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
DisableNesting: false,
|
||||
},
|
||||
},
|
||||
AllowMutations: []graphql.Queries{},
|
||||
}
|
||||
|
||||
if cmd.Bool(flagWithMutations) {
|
||||
filter.AllowMutations = []graphql.Queries{
|
||||
{
|
||||
Name: "updateConfig",
|
||||
DisableNesting: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
schema := graphql.ParseSchema(introspection, filter)
|
||||
|
||||
fmt.Print(schema) //nolint:forbidigo
|
||||
|
||||
return nil
|
||||
}
|
||||
33
cli/cmd/mcp/mcp.go
Normal file
33
cli/cmd/mcp/mcp.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package mcp
|
||||
|
||||
import (
|
||||
"github.com/nhost/nhost/cli/cmd/mcp/config"
|
||||
"github.com/nhost/nhost/cli/cmd/mcp/gen"
|
||||
"github.com/nhost/nhost/cli/cmd/mcp/start"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
flagConfigFile = "config-file"
|
||||
)
|
||||
|
||||
func Command() *cli.Command {
|
||||
return &cli.Command{ //nolint:exhaustruct
|
||||
Name: "mcp",
|
||||
Aliases: []string{},
|
||||
Usage: "Model Context Protocol (MCP) related commands",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagConfigFile,
|
||||
Usage: "Configuration file path. Defaults to $NHOST_DOT_NHOST_FOLDER/nhost-mcp.toml",
|
||||
Value: "",
|
||||
Sources: cli.EnvVars("NHOST_MCP_CONFIG_FILE"),
|
||||
},
|
||||
},
|
||||
Commands: []*cli.Command{
|
||||
config.Command(),
|
||||
start.Command(),
|
||||
gen.Command(),
|
||||
},
|
||||
}
|
||||
}
|
||||
447
cli/cmd/mcp/mcp_test.go
Normal file
447
cli/cmd/mcp/mcp_test.go
Normal file
@@ -0,0 +1,447 @@
|
||||
package mcp_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/mark3labs/mcp-go/client"
|
||||
"github.com/mark3labs/mcp-go/client/transport"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
nhostmcp "github.com/nhost/nhost/cli/cmd/mcp"
|
||||
"github.com/nhost/nhost/cli/cmd/mcp/start"
|
||||
"github.com/nhost/nhost/cli/cmd/user"
|
||||
"github.com/nhost/nhost/cli/mcp/tools/cloud"
|
||||
"github.com/nhost/nhost/cli/mcp/tools/docs"
|
||||
"github.com/nhost/nhost/cli/mcp/tools/local"
|
||||
"github.com/nhost/nhost/cli/mcp/tools/project"
|
||||
)
|
||||
|
||||
func ptr[T any](v T) *T {
|
||||
return &v
|
||||
}
|
||||
|
||||
func TestStart(t *testing.T) { //nolint:cyclop,maintidx,paralleltest
|
||||
loginCmd := user.CommandLogin()
|
||||
mcpCmd := nhostmcp.Command()
|
||||
|
||||
buf := bytes.NewBuffer(nil)
|
||||
mcpCmd.Writer = buf
|
||||
|
||||
go func() {
|
||||
t.Setenv("HOME", t.TempDir())
|
||||
|
||||
if err := loginCmd.Run(
|
||||
context.Background(),
|
||||
[]string{
|
||||
"main",
|
||||
"--pat=user-pat",
|
||||
},
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err := mcpCmd.Run(
|
||||
context.Background(),
|
||||
[]string{
|
||||
"main",
|
||||
"start",
|
||||
"--bind=:9000",
|
||||
"--config-file=testdata/sample.toml",
|
||||
},
|
||||
); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
transportClient, err := transport.NewSSE("http://localhost:9000/sse")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create transport client: %v", err)
|
||||
}
|
||||
|
||||
mcpClient := client.NewClient(transportClient)
|
||||
|
||||
if err := mcpClient.Start(t.Context()); err != nil {
|
||||
t.Fatalf("failed to start mcp client: %v", err)
|
||||
}
|
||||
defer mcpClient.Close()
|
||||
|
||||
initRequest := mcp.InitializeRequest{} //nolint:exhaustruct
|
||||
initRequest.Params.ProtocolVersion = mcp.LATEST_PROTOCOL_VERSION
|
||||
initRequest.Params.ClientInfo = mcp.Implementation{
|
||||
Name: "example-client",
|
||||
Version: "1.0.0",
|
||||
}
|
||||
|
||||
res, err := mcpClient.Initialize(
|
||||
context.Background(),
|
||||
initRequest,
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to initialize mcp client: %v", err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(
|
||||
res,
|
||||
//nolint:tagalign
|
||||
&mcp.InitializeResult{
|
||||
ProtocolVersion: "2025-06-18",
|
||||
Capabilities: mcp.ServerCapabilities{
|
||||
Elicitation: nil,
|
||||
Experimental: nil,
|
||||
Logging: nil,
|
||||
Prompts: nil,
|
||||
Resources: nil,
|
||||
Sampling: nil,
|
||||
Tools: &struct {
|
||||
ListChanged bool "json:\"listChanged,omitempty\""
|
||||
}{
|
||||
ListChanged: true,
|
||||
},
|
||||
},
|
||||
ServerInfo: mcp.Implementation{
|
||||
Name: "mcp",
|
||||
Version: "",
|
||||
},
|
||||
Instructions: start.ServerInstructions,
|
||||
Result: mcp.Result{
|
||||
Meta: nil,
|
||||
},
|
||||
},
|
||||
); diff != "" {
|
||||
t.Errorf("ServerInfo mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
tools, err := mcpClient.ListTools(
|
||||
context.Background(),
|
||||
mcp.ListToolsRequest{}, //nolint:exhaustruct
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list tools: %v", err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(
|
||||
tools,
|
||||
//nolint:exhaustruct,lll
|
||||
&mcp.ListToolsResult{
|
||||
Tools: []mcp.Tool{
|
||||
{
|
||||
Name: "cloud-get-graphql-schema",
|
||||
Description: cloud.ToolGetGraphqlSchemaInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: nil,
|
||||
Required: nil,
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Get GraphQL Schema for Nhost Cloud Platform",
|
||||
ReadOnlyHint: ptr(true),
|
||||
DestructiveHint: ptr(false),
|
||||
IdempotentHint: ptr(true),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "cloud-graphql-query",
|
||||
Description: cloud.ToolGraphqlQueryInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"query": map[string]any{
|
||||
"description": "graphql query to perform",
|
||||
"type": "string",
|
||||
},
|
||||
"variables": map[string]any{
|
||||
"description": "variables to use in the query",
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
Required: []string{"query"},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Perform GraphQL Query on Nhost Cloud Platform",
|
||||
ReadOnlyHint: ptr(false),
|
||||
DestructiveHint: ptr(true),
|
||||
IdempotentHint: ptr(false),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "local-config-server-get-schema",
|
||||
Description: local.ToolConfigServerSchemaInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"includeMutations": map[string]any{
|
||||
"description": "include mutations in the schema",
|
||||
"type": "boolean",
|
||||
},
|
||||
"includeQueries": map[string]any{
|
||||
"description": "include queries in the schema",
|
||||
"type": "boolean",
|
||||
},
|
||||
},
|
||||
Required: []string{"includeQueries", "includeMutations"},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Get GraphQL Schema for Nhost Config Server",
|
||||
ReadOnlyHint: ptr(true),
|
||||
DestructiveHint: ptr(false),
|
||||
IdempotentHint: ptr(true),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "local-config-server-query",
|
||||
Description: local.ToolConfigServerQueryInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"query": map[string]any{
|
||||
"description": "graphql query to perform",
|
||||
"type": "string",
|
||||
},
|
||||
"variables": map[string]any{
|
||||
"description": "variables to use in the query",
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
Required: []string{"query"},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Perform GraphQL Query on Nhost Config Server",
|
||||
ReadOnlyHint: ptr(false),
|
||||
DestructiveHint: ptr(true),
|
||||
IdempotentHint: ptr(false),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "local-get-graphql-schema",
|
||||
Description: local.ToolGetGraphqlSchemaInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"role": map[string]any{
|
||||
"description": "role to use when executing queries. Default to user but make sure the user is aware",
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
Required: []string{"role"},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Get GraphQL Schema for Nhost Development Project",
|
||||
ReadOnlyHint: ptr(true),
|
||||
DestructiveHint: ptr(false),
|
||||
IdempotentHint: ptr(true),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "local-graphql-query",
|
||||
Description: local.ToolGraphqlQueryInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"query": map[string]any{
|
||||
"description": "graphql query to perform",
|
||||
"type": "string",
|
||||
},
|
||||
"role": map[string]any{
|
||||
"description": "role to use when executing queries. Default to user but make sure the user is aware. Keep in mind the schema depends on the role so if you retrieved the schema for a different role previously retrieve it for this role beforehand as it might differ",
|
||||
"type": "string",
|
||||
},
|
||||
"variables": map[string]any{
|
||||
"additionalProperties": true,
|
||||
"description": "variables to use in the query",
|
||||
"properties": map[string]any{},
|
||||
"type": "object",
|
||||
},
|
||||
},
|
||||
Required: []string{"query", "role"},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Perform GraphQL Query on Nhost Development Project",
|
||||
ReadOnlyHint: ptr(false),
|
||||
DestructiveHint: ptr(true),
|
||||
IdempotentHint: ptr(false),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "project-get-graphql-schema",
|
||||
Description: project.ToolGetGraphqlSchemaInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"projectSubdomain": map[string]any{
|
||||
"description": "Project to get the GraphQL schema for. Must be one of asdasdasdasdasd, qweqweqweqweqwe, otherwise you don't have access to it. You can use cloud-* tools to resolve subdomains and map them to names",
|
||||
"type": "string",
|
||||
},
|
||||
"role": map[string]any{
|
||||
"description": "role to use when executing queries. Default to user but make sure the user is aware",
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
Required: []string{"role", "projectSubdomain"},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Get GraphQL Schema for Nhost Project running on Nhost Cloud",
|
||||
ReadOnlyHint: ptr(true),
|
||||
DestructiveHint: ptr(false),
|
||||
IdempotentHint: ptr(true),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "project-graphql-query",
|
||||
Description: project.ToolGraphqlQueryInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"query": map[string]any{
|
||||
"description": "graphql query to perform",
|
||||
"type": "string",
|
||||
},
|
||||
"projectSubdomain": map[string]any{
|
||||
"description": "Project to get the GraphQL schema for. Must be one of asdasdasdasdasd, qweqweqweqweqwe, otherwise you don't have access to it. You can use cloud-* tools to resolve subdomains and map them to names",
|
||||
"type": "string",
|
||||
},
|
||||
"role": map[string]any{
|
||||
"description": "role to use when executing queries. Default to user but make sure the user is aware. Keep in mind the schema depends on the role so if you retrieved the schema for a different role previously retrieve it for this role beforehand as it might differ",
|
||||
"type": "string",
|
||||
},
|
||||
"userId": map[string]any{
|
||||
"description": string("Overrides X-Hasura-User-Id in the GraphQL query/mutation. Credentials must allow it (i.e. admin secret must be in use)"),
|
||||
"type": string("string"),
|
||||
},
|
||||
"variables": map[string]any{
|
||||
"description": "variables to use in the query",
|
||||
"type": "string",
|
||||
},
|
||||
},
|
||||
Required: []string{"query", "projectSubdomain", "role"},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Perform GraphQL Query on Nhost Project running on Nhost Cloud",
|
||||
ReadOnlyHint: ptr(false),
|
||||
DestructiveHint: ptr(true),
|
||||
IdempotentHint: ptr(false),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "local-get-management-graphql-schema",
|
||||
Description: local.ToolGetGraphqlManagementSchemaInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: nil,
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Get GraphQL's Management Schema for Nhost Development Project",
|
||||
ReadOnlyHint: ptr(true),
|
||||
IdempotentHint: ptr(true),
|
||||
DestructiveHint: ptr(false),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "local-manage-graphql",
|
||||
Description: local.ToolManageGraphqlInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"body": map[string]any{
|
||||
"description": string("The body for the HTTP request"),
|
||||
"type": string("string"),
|
||||
},
|
||||
"endpoint": map[string]any{
|
||||
"description": string("The GraphQL management endpoint to query. Use https://local.hasura.local.nhost.run as base URL"),
|
||||
"type": string("string"),
|
||||
},
|
||||
},
|
||||
Required: []string{"endpoint", "body"},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Manage GraphQL's Metadata on an Nhost Development Project",
|
||||
ReadOnlyHint: ptr(false),
|
||||
DestructiveHint: ptr(true),
|
||||
IdempotentHint: ptr(true),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "search",
|
||||
Description: docs.ToolSearchInstructions,
|
||||
InputSchema: mcp.ToolInputSchema{
|
||||
Type: "object",
|
||||
Properties: map[string]any{
|
||||
"query": map[string]any{
|
||||
"description": string("The search query"),
|
||||
"type": string("string"),
|
||||
},
|
||||
},
|
||||
Required: []string{"query"},
|
||||
},
|
||||
Annotations: mcp.ToolAnnotation{
|
||||
Title: "Search Nhost Docs",
|
||||
ReadOnlyHint: ptr(true),
|
||||
IdempotentHint: ptr(true),
|
||||
DestructiveHint: ptr(false),
|
||||
OpenWorldHint: ptr(true),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
cmpopts.SortSlices(func(a, b mcp.Tool) bool {
|
||||
return a.Name < b.Name
|
||||
}),
|
||||
); diff != "" {
|
||||
t.Errorf("ListToolsResult mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
if res.Capabilities.Resources != nil {
|
||||
resources, err := mcpClient.ListResources(
|
||||
context.Background(),
|
||||
mcp.ListResourcesRequest{}, //nolint:exhaustruct
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list resources: %v", err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(
|
||||
resources,
|
||||
//nolint:exhaustruct
|
||||
&mcp.ListResourcesResult{
|
||||
Resources: []mcp.Resource{},
|
||||
},
|
||||
); diff != "" {
|
||||
t.Errorf("ListResourcesResult mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
if res.Capabilities.Prompts != nil {
|
||||
prompts, err := mcpClient.ListPrompts(
|
||||
context.Background(),
|
||||
mcp.ListPromptsRequest{}, //nolint:exhaustruct
|
||||
)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list prompts: %v", err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(
|
||||
prompts,
|
||||
//nolint:exhaustruct
|
||||
&mcp.ListPromptsResult{
|
||||
Prompts: []mcp.Prompt{},
|
||||
},
|
||||
); diff != "" {
|
||||
t.Errorf("ListPromptsResult mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
223
cli/cmd/mcp/start/start.go
Normal file
223
cli/cmd/mcp/start/start.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package start
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/mark3labs/mcp-go/server"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/mcp/config"
|
||||
"github.com/nhost/nhost/cli/mcp/nhost/auth"
|
||||
"github.com/nhost/nhost/cli/mcp/tools/cloud"
|
||||
"github.com/nhost/nhost/cli/mcp/tools/docs"
|
||||
"github.com/nhost/nhost/cli/mcp/tools/local"
|
||||
"github.com/nhost/nhost/cli/mcp/tools/project"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
flagNhostAuthURL = "nhost-auth-url"
|
||||
flagNhostGraphqlURL = "nhost-graphql-url"
|
||||
flagBind = "bind"
|
||||
)
|
||||
|
||||
const (
|
||||
// this seems to be largely ignored by clients, or at least by cursor.
|
||||
// we also need to look into roots and resources as those might be helpful.
|
||||
ServerInstructions = `
|
||||
This is an MCP server to interact with Nhost Cloud and with projects running on it and
|
||||
also with Nhost local development projects.
|
||||
|
||||
Important notes to anyone using this MCP server. Do not use this MCP server without
|
||||
following these instructions:
|
||||
|
||||
1. Make sure you are clear on which environment the user wants to operate against.
|
||||
2. Before attempting to call any tool *-graphql-query, always get the schema using the
|
||||
*-get-graphql-schema tool
|
||||
3. Apps and projects are the same and while users may talk about projects in the GraphQL
|
||||
api those are referred as apps.
|
||||
4. IDs are always UUIDs so if you have anything else (like an app/project name) you may need
|
||||
to first get the ID using the *-graphql-query tool.
|
||||
5. If you have an error querying the GraphQL API, please check the schema again. The schema may
|
||||
have changed and the query you are using may be invalid.
|
||||
`
|
||||
)
|
||||
|
||||
func Command() *cli.Command {
|
||||
return &cli.Command{ //nolint:exhaustruct
|
||||
Name: "start",
|
||||
Usage: "Starts the MCP server",
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagNhostAuthURL,
|
||||
Usage: "Nhost auth URL",
|
||||
Hidden: true,
|
||||
Value: "https://otsispdzcwxyqzbfntmj.auth.eu-central-1.nhost.run/v1",
|
||||
Category: "Cloud Platform",
|
||||
Sources: cli.EnvVars("NHOST_AUTH_URL"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagNhostGraphqlURL,
|
||||
Usage: "Nhost GraphQL URL",
|
||||
Hidden: true,
|
||||
Value: "https://otsispdzcwxyqzbfntmj.graphql.eu-central-1.nhost.run/v1",
|
||||
Category: "Cloud Platform",
|
||||
Sources: cli.EnvVars("NHOST_GRAPHQL_URL"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagBind,
|
||||
Usage: "Bind address in the form $host:$port. If omitted use stdio",
|
||||
Required: false,
|
||||
Category: "General",
|
||||
Sources: cli.EnvVars("BIND"),
|
||||
},
|
||||
},
|
||||
Action: action,
|
||||
}
|
||||
}
|
||||
|
||||
func action(ctx context.Context, cmd *cli.Command) error {
|
||||
cfg, err := getConfig(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mcpServer := server.NewMCPServer(
|
||||
cmd.Root().Name,
|
||||
cmd.Root().Version,
|
||||
server.WithInstructions(ServerInstructions),
|
||||
)
|
||||
|
||||
if cfg.Cloud != nil {
|
||||
if err := registerCloud(
|
||||
cmd,
|
||||
mcpServer,
|
||||
cfg,
|
||||
cmd.String(flagNhostAuthURL),
|
||||
cmd.String(flagNhostGraphqlURL),
|
||||
); err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to register cloud tools: %s", err), 1)
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Local != nil {
|
||||
if err := registerLocal(mcpServer, cfg); err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to register local tools: %s", err), 1)
|
||||
}
|
||||
}
|
||||
|
||||
if len(cfg.Projects) > 0 {
|
||||
if err := registerProjectTool(mcpServer, cfg); err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to register project tools: %s", err), 1)
|
||||
}
|
||||
}
|
||||
|
||||
d, err := docs.NewTool(ctx)
|
||||
if err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to initialize docs tools: %s", err), 1)
|
||||
}
|
||||
|
||||
d.Register(mcpServer)
|
||||
|
||||
return start(mcpServer, cmd.String(flagBind))
|
||||
}
|
||||
|
||||
func getConfig(cmd *cli.Command) (*config.Config, error) {
|
||||
configPath := config.GetConfigPath(cmd)
|
||||
if configPath == "" {
|
||||
return nil, cli.Exit("config file path is required", 1)
|
||||
}
|
||||
|
||||
cfg, err := config.Load(configPath)
|
||||
if err != nil {
|
||||
fmt.Println("Please, run `mcp-nhost config` to configure the service.") //nolint:forbidigo
|
||||
return nil, cli.Exit("failed to load config file "+err.Error(), 1)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func registerCloud(
|
||||
cmd *cli.Command,
|
||||
mcpServer *server.MCPServer,
|
||||
cfg *config.Config,
|
||||
authURL string,
|
||||
graphqlURL string,
|
||||
) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
creds, err := ce.Credentials()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load credentials: %w", err)
|
||||
}
|
||||
|
||||
interceptor, err := auth.WithPAT(
|
||||
authURL,
|
||||
creds.PersonalAccessToken,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create PAT interceptor: %w", err)
|
||||
}
|
||||
|
||||
cloudTool := cloud.NewTool(
|
||||
graphqlURL, cfg.Cloud.EnableMutations, interceptor,
|
||||
)
|
||||
|
||||
if err := cloudTool.Register(mcpServer); err != nil {
|
||||
return fmt.Errorf("failed to register tools: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerLocal(
|
||||
mcpServer *server.MCPServer,
|
||||
cfg *config.Config,
|
||||
) error {
|
||||
interceptor := auth.WithAdminSecret(cfg.Local.AdminSecret)
|
||||
|
||||
localTool := local.NewTool(
|
||||
*cfg.Local.GraphqlURL,
|
||||
*cfg.Local.ConfigServerURL,
|
||||
interceptor,
|
||||
)
|
||||
if err := localTool.Register(mcpServer); err != nil {
|
||||
return fmt.Errorf("failed to register tools: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func registerProjectTool(
|
||||
mcpServer *server.MCPServer,
|
||||
cfg *config.Config,
|
||||
) error {
|
||||
projectTool, err := project.NewTool(cfg.Projects)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to initialize tool: %w", err)
|
||||
}
|
||||
|
||||
if err := projectTool.Register(mcpServer); err != nil {
|
||||
return fmt.Errorf("failed to register tool: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func start(
|
||||
mcpServer *server.MCPServer,
|
||||
bind string,
|
||||
) error {
|
||||
if bind != "" {
|
||||
sseServer := server.NewSSEServer(mcpServer, server.WithBaseURL(bind))
|
||||
if err := sseServer.Start(bind); err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to serve tcp: %v", err), 1)
|
||||
}
|
||||
} else {
|
||||
if err := server.ServeStdio(mcpServer); err != nil {
|
||||
return cli.Exit(fmt.Sprintf("failed to serve stdio: %v", err), 1)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
19
cli/cmd/mcp/testdata/sample.toml
vendored
Normal file
19
cli/cmd/mcp/testdata/sample.toml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
[cloud]
|
||||
enable_mutations = true
|
||||
|
||||
[local]
|
||||
admin_secret = 'nhost-admin-secret'
|
||||
|
||||
[[projects]]
|
||||
subdomain = 'asdasdasdasdasd'
|
||||
region = 'eu-central-1'
|
||||
admin_secret = 'your-admin-secret-1'
|
||||
allow_queries = ['*']
|
||||
allow_mutations = ['*']
|
||||
|
||||
[[projects]]
|
||||
subdomain = 'qweqweqweqweqwe'
|
||||
region = 'us-east-1'
|
||||
pat = 'pat-for-qweqweqweqweqwe'
|
||||
allow_queries = ['getComments']
|
||||
allow_mutations = ['insertComment', 'updateComment', 'deleteComment']
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/hashicorp/go-getter"
|
||||
"github.com/hashicorp/go-getter/v2"
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/cmd/config"
|
||||
"github.com/nhost/nhost/cli/dockercompose"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -75,14 +75,14 @@ func CommandInit() *cli.Command {
|
||||
Name: flagRemote,
|
||||
Usage: "Initialize pulling configuration, migrations and metadata from the linked project",
|
||||
Value: false,
|
||||
EnvVars: []string{"NHOST_REMOTE"},
|
||||
Sources: cli.EnvVars("NHOST_REMOTE"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func commandInit(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandInit(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
if clienv.PathExists(ce.Path.NhostFolder()) {
|
||||
return errors.New("nhost folder already exists") //nolint:err113
|
||||
@@ -98,12 +98,12 @@ func commandInit(cCtx *cli.Context) error {
|
||||
return fmt.Errorf("failed to initialize configuration: %w", err)
|
||||
}
|
||||
|
||||
if cCtx.Bool(flagRemote) {
|
||||
if err := InitRemote(cCtx.Context, ce); err != nil {
|
||||
if cmd.Bool(flagRemote) {
|
||||
if err := InitRemote(ctx, ce); err != nil {
|
||||
return fmt.Errorf("failed to initialize remote project: %w", err)
|
||||
}
|
||||
} else {
|
||||
if err := initInit(cCtx.Context, ce.Path); err != nil {
|
||||
if err := initInit(ctx, ce.Path); err != nil {
|
||||
return fmt.Errorf("failed to initialize project: %w", err)
|
||||
}
|
||||
}
|
||||
@@ -129,17 +129,12 @@ func initInit(
|
||||
return err
|
||||
}
|
||||
|
||||
getclient := &getter.Client{ //nolint:exhaustruct
|
||||
Ctx: ctx,
|
||||
Src: "github.com/nhost/hasura-auth/email-templates",
|
||||
Dst: "nhost/emails",
|
||||
Mode: getter.ClientModeAny,
|
||||
Detectors: []getter.Detector{
|
||||
&getter.GitHubDetector{},
|
||||
},
|
||||
}
|
||||
|
||||
if err := getclient.Get(); err != nil {
|
||||
getclient := &getter.Client{} //nolint:exhaustruct
|
||||
if _, err := getclient.Get(ctx, &getter.Request{ //nolint:exhaustruct
|
||||
Src: "git::https://github.com/nhost/hasura-auth.git//email-templates",
|
||||
Dst: "nhost/emails",
|
||||
DisableSymlinks: true,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("failed to download email templates: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package project
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandLink() *cli.Command {
|
||||
@@ -18,14 +19,14 @@ func CommandLink() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandLink(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandLink(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
if err := os.MkdirAll(ce.Path.DotNhostFolder(), 0o755); err != nil { //nolint:mnd
|
||||
return fmt.Errorf("failed to create .nhost folder: %w", err)
|
||||
}
|
||||
|
||||
_, err := ce.Link(cCtx.Context)
|
||||
_, err := ce.Link(ctx)
|
||||
|
||||
return err //nolint:wrapcheck
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandList() *cli.Command {
|
||||
@@ -18,9 +18,9 @@ func CommandList() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandList(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
return List(cCtx.Context, ce)
|
||||
func commandList(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
return List(ctx, ce)
|
||||
}
|
||||
|
||||
func List(ctx context.Context, ce *clienv.CliEnv) error {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/nhostclient/graphql"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandConfigDeploy() *cli.Command {
|
||||
@@ -23,13 +24,13 @@ func CommandConfigDeploy() *cli.Command {
|
||||
Usage: "Service configuration file",
|
||||
Value: "nhost-run-service.toml",
|
||||
Required: true,
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_CONFIG"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_CONFIG"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagServiceID,
|
||||
Usage: "Service ID to update. Applies overlay of the same name",
|
||||
Required: true,
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_ID"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_ID"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -49,23 +50,23 @@ func transform[T, V any](t *T) (*V, error) {
|
||||
return &v, nil
|
||||
}
|
||||
|
||||
func commandConfigDeploy(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandConfigDeploy(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
cl, err := ce.GetNhostClient(cCtx.Context)
|
||||
cl, err := ce.GetNhostClient(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nhost client: %w", err)
|
||||
}
|
||||
|
||||
secrets, appID, err := getRemoteSecrets(cCtx.Context, cl, cCtx.String(flagServiceID))
|
||||
secrets, appID, err := getRemoteSecrets(ctx, cl, cmd.String(flagServiceID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg, err := Validate(
|
||||
ce,
|
||||
cCtx.String(flagConfig),
|
||||
cCtx.String(flagServiceID),
|
||||
cmd.String(flagConfig),
|
||||
cmd.String(flagServiceID),
|
||||
secrets,
|
||||
true,
|
||||
)
|
||||
@@ -81,9 +82,9 @@ func commandConfigDeploy(cCtx *cli.Context) error {
|
||||
}
|
||||
|
||||
if _, err := cl.ReplaceRunServiceConfig(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
appID,
|
||||
cCtx.String(flagServiceID),
|
||||
cmd.String(flagServiceID),
|
||||
*replaceConfig,
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to replace service config: %w", err)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -8,7 +9,7 @@ import (
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/cmd/config"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const flagEditor = "editor"
|
||||
@@ -25,31 +26,31 @@ func CommandConfigEdit() *cli.Command {
|
||||
Usage: "Service configuration file",
|
||||
Value: "nhost-run-service.toml",
|
||||
Required: true,
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_CONFIG"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_CONFIG"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagEditor,
|
||||
Usage: "Editor to use",
|
||||
Value: "vim",
|
||||
EnvVars: []string{"EDITOR"},
|
||||
Sources: cli.EnvVars("EDITOR"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagOverlayName,
|
||||
Usage: "If specified, apply this overlay",
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_ID", "NHOST_SERVICE_OVERLAY_NAME"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_ID", "NHOST_SERVICE_OVERLAY_NAME"),
|
||||
},
|
||||
},
|
||||
Action: commandConfigEdit,
|
||||
}
|
||||
}
|
||||
|
||||
func commandConfigEdit(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandConfigEdit(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
overlayName := cCtx.String(flagOverlayName)
|
||||
overlayName := cmd.String(flagOverlayName)
|
||||
if overlayName == "" {
|
||||
if err := config.EditFile(
|
||||
cCtx.Context, cCtx.String(flagEditor), cCtx.String(flagConfig),
|
||||
ctx, cmd.String(flagEditor), cmd.String(flagConfig),
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to edit config: %w", err)
|
||||
}
|
||||
@@ -58,7 +59,7 @@ func commandConfigEdit(cCtx *cli.Context) error {
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(ce.Path.RunServiceOverlaysFolder(
|
||||
cCtx.String(flagConfig),
|
||||
cmd.String(flagConfig),
|
||||
), 0o755); err != nil { //nolint:mnd
|
||||
return fmt.Errorf("failed to create json patches directory: %w", err)
|
||||
}
|
||||
@@ -72,21 +73,21 @@ func commandConfigEdit(cCtx *cli.Context) error {
|
||||
tmpfileName := filepath.Join(tmpdir, "nhost.toml")
|
||||
|
||||
if err := config.CopyConfig[model.ConfigRunServiceConfig](
|
||||
cCtx.String(flagConfig),
|
||||
cmd.String(flagConfig),
|
||||
tmpfileName,
|
||||
ce.Path.RunServiceOverlay(cCtx.String(flagConfig), overlayName),
|
||||
ce.Path.RunServiceOverlay(cmd.String(flagConfig), overlayName),
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to copy config: %w", err)
|
||||
}
|
||||
|
||||
if err := config.EditFile(cCtx.Context, cCtx.String(flagEditor), tmpfileName); err != nil {
|
||||
if err := config.EditFile(ctx, cmd.String(flagEditor), tmpfileName); err != nil {
|
||||
return fmt.Errorf("failed to edit config: %w", err)
|
||||
}
|
||||
|
||||
if err := config.GenerateJSONPatch(
|
||||
cCtx.String(flagConfig),
|
||||
cmd.String(flagConfig),
|
||||
tmpfileName,
|
||||
ce.Path.RunServiceOverlay(cCtx.String(flagConfig), overlayName),
|
||||
ce.Path.RunServiceOverlay(cmd.String(flagConfig), overlayName),
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to generate json patch: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const flagImage = "image"
|
||||
@@ -22,29 +23,29 @@ func CommandConfigEditImage() *cli.Command {
|
||||
Aliases: []string{},
|
||||
Usage: "Service configuration file",
|
||||
Value: "nhost-run-service.toml",
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_CONFIG"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_CONFIG"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagImage,
|
||||
Aliases: []string{},
|
||||
Usage: "Image to use",
|
||||
Required: true,
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_IMAGE"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_IMAGE"),
|
||||
},
|
||||
},
|
||||
Action: commandConfigEditImage,
|
||||
}
|
||||
}
|
||||
|
||||
func commandConfigEditImage(cCtx *cli.Context) error {
|
||||
func commandConfigEditImage(_ context.Context, cmd *cli.Command) error {
|
||||
var cfg model.ConfigRunServiceConfig
|
||||
if err := clienv.UnmarshalFile(cCtx.String(flagConfig), &cfg, toml.Unmarshal); err != nil {
|
||||
if err := clienv.UnmarshalFile(cmd.String(flagConfig), &cfg, toml.Unmarshal); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
|
||||
cfg.Image.Image = cCtx.String(flagImage)
|
||||
cfg.Image.Image = cmd.String(flagImage)
|
||||
|
||||
if err := clienv.MarshalFile(cfg, cCtx.String(flagConfig), toml.Marshal); err != nil {
|
||||
if err := clienv.MarshalFile(cfg, cmd.String(flagConfig), toml.Marshal); err != nil {
|
||||
return fmt.Errorf("failed to marshal config: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/be/services/mimir/schema"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func ptr[T any](v T) *T {
|
||||
@@ -24,8 +25,8 @@ func CommandConfigExample() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandConfigExample(cCtx *cli.Context) error { //nolint:funlen
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandConfigExample(_ context.Context, cmd *cli.Command) error { //nolint:funlen
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
//nolint:mnd
|
||||
cfg := &model.ConfigRunServiceConfig{
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const flagServiceID = "service-id"
|
||||
@@ -23,36 +24,36 @@ func CommandConfigPull() *cli.Command {
|
||||
Aliases: []string{},
|
||||
Usage: "Service configuration file",
|
||||
Value: "nhost-run-service.toml",
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_CONFIG"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_CONFIG"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagServiceID,
|
||||
Usage: "Service ID to update",
|
||||
Required: true,
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_ID"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_ID"),
|
||||
},
|
||||
},
|
||||
Action: commandConfigPull,
|
||||
}
|
||||
}
|
||||
|
||||
func commandConfigPull(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandConfigPull(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
cl, err := ce.GetNhostClient(cCtx.Context)
|
||||
cl, err := ce.GetNhostClient(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nhost client: %w", err)
|
||||
}
|
||||
|
||||
appID, err := getAppIDFromServiceID(cCtx.Context, cl, cCtx.String(flagServiceID))
|
||||
appID, err := getAppIDFromServiceID(ctx, cl, cmd.String(flagServiceID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := cl.GetRunServiceConfigRawJSON(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
appID,
|
||||
cCtx.String(flagServiceID),
|
||||
cmd.String(flagServiceID),
|
||||
false,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -64,7 +65,7 @@ func commandConfigPull(cCtx *cli.Context) error {
|
||||
return fmt.Errorf("failed to unmarshal config: %w", err)
|
||||
}
|
||||
|
||||
if err := clienv.MarshalFile(v, cCtx.String(flagConfig), toml.Marshal); err != nil {
|
||||
if err := clienv.MarshalFile(v, cmd.String(flagConfig), toml.Marshal); err != nil {
|
||||
return fmt.Errorf("failed to save config to file: %w", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/project/env"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandConfigShow() *cli.Command {
|
||||
@@ -23,19 +24,19 @@ func CommandConfigShow() *cli.Command {
|
||||
Aliases: []string{},
|
||||
Usage: "Service configuration file",
|
||||
Value: "nhost-run-service.toml",
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_CONFIG"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_CONFIG"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagOverlayName,
|
||||
Usage: "If specified, apply this overlay",
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_ID", "NHOST_SERVICE_OVERLAY_NAME"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_ID", "NHOST_SERVICE_OVERLAY_NAME"),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func commandConfigShow(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandConfigShow(_ context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
var secrets model.Secrets
|
||||
if err := clienv.UnmarshalFile(ce.Path.Secrets(), &secrets, env.Unmarshal); err != nil {
|
||||
@@ -47,8 +48,8 @@ func commandConfigShow(cCtx *cli.Context) error {
|
||||
|
||||
cfg, err := Validate(
|
||||
ce,
|
||||
cCtx.String(flagConfig),
|
||||
cCtx.String(flagOverlayName),
|
||||
cmd.String(flagConfig),
|
||||
cmd.String(flagOverlayName),
|
||||
secrets,
|
||||
false,
|
||||
)
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
"github.com/nhost/nhost/cli/nhostclient/graphql"
|
||||
"github.com/nhost/nhost/cli/project/env"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -35,17 +35,17 @@ func CommandConfigValidate() *cli.Command {
|
||||
Aliases: []string{},
|
||||
Usage: "Service configuration file",
|
||||
Value: "nhost-run-service.toml",
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_CONFIG"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_CONFIG"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagOverlayName,
|
||||
Usage: "If specified, apply this overlay",
|
||||
EnvVars: []string{"NHOST_SERVICE_OVERLAY_NAME"},
|
||||
Sources: cli.EnvVars("NHOST_SERVICE_OVERLAY_NAME"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagServiceID,
|
||||
Usage: "If specified, apply this overlay and remote secrets for this service",
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_ID"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_ID"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -157,35 +157,35 @@ func getRemoteSecrets(
|
||||
return respToSecrets(secretsResp.GetAppSecrets()), appID, nil
|
||||
}
|
||||
|
||||
func commandConfigValidate(cCtx *cli.Context) error {
|
||||
func commandConfigValidate(ctx context.Context, cmd *cli.Command) error {
|
||||
var (
|
||||
overlayName string
|
||||
serviceID string
|
||||
)
|
||||
|
||||
switch {
|
||||
case cCtx.String(flagServiceID) != "" && cCtx.String(flagOverlayName) != "":
|
||||
case cmd.String(flagServiceID) != "" && cmd.String(flagOverlayName) != "":
|
||||
return errors.New("cannot specify both service id and overlay name") //nolint:err113
|
||||
case cCtx.String(flagServiceID) != "":
|
||||
serviceID = cCtx.String(flagServiceID)
|
||||
case cmd.String(flagServiceID) != "":
|
||||
serviceID = cmd.String(flagServiceID)
|
||||
overlayName = serviceID
|
||||
case cCtx.String(flagOverlayName) != "":
|
||||
overlayName = cCtx.String(flagOverlayName)
|
||||
case cmd.String(flagOverlayName) != "":
|
||||
overlayName = cmd.String(flagOverlayName)
|
||||
}
|
||||
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
var secrets model.Secrets
|
||||
|
||||
ce.Infoln("Getting secrets...")
|
||||
|
||||
if serviceID != "" {
|
||||
cl, err := ce.GetNhostClient(cCtx.Context)
|
||||
cl, err := ce.GetNhostClient(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nhost client: %w", err)
|
||||
}
|
||||
|
||||
secrets, _, err = getRemoteSecrets(cCtx.Context, cl, serviceID)
|
||||
secrets, _, err = getRemoteSecrets(ctx, cl, serviceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -202,7 +202,7 @@ func commandConfigValidate(cCtx *cli.Context) error {
|
||||
|
||||
if _, err := Validate(
|
||||
ce,
|
||||
cCtx.String(flagConfig),
|
||||
cmd.String(flagConfig),
|
||||
overlayName,
|
||||
secrets,
|
||||
false,
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
|
||||
"github.com/nhost/be/services/mimir/model"
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/nhost/nhost/cli/project/env"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,17 +29,17 @@ func CommandEnv() *cli.Command {
|
||||
Aliases: []string{},
|
||||
Usage: "Service configuration file",
|
||||
Value: "nhost-run-service.toml",
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_CONFIG"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_CONFIG"),
|
||||
},
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagOverlayName,
|
||||
Usage: "If specified, apply this overlay",
|
||||
EnvVars: []string{"NHOST_RUN_SERVICE_ID", "NHOST_SERVICE_OVERLAY_NAME"},
|
||||
Sources: cli.EnvVars("NHOST_RUN_SERVICE_ID", "NHOST_SERVICE_OVERLAY_NAME"),
|
||||
},
|
||||
&cli.BoolFlag{ //nolint:exhaustruct
|
||||
Name: flagDevPrependExport,
|
||||
Usage: "Prepend 'export' to each line",
|
||||
EnvVars: []string{"NHOST_RuN_SERVICE_ENV_PREPEND_EXPORT"},
|
||||
Sources: cli.EnvVars("NHOST_RuN_SERVICE_ENV_PREPEND_EXPORT"),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -49,8 +50,8 @@ func escape(s string) string {
|
||||
return re.ReplaceAllString(s, "\\$0")
|
||||
}
|
||||
|
||||
func commandConfigDev(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandConfigDev(_ context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
var secrets model.Secrets
|
||||
if err := clienv.UnmarshalFile(ce.Path.Secrets(), &secrets, env.Unmarshal); err != nil {
|
||||
@@ -62,8 +63,8 @@ func commandConfigDev(cCtx *cli.Context) error {
|
||||
|
||||
cfg, err := Validate(
|
||||
ce,
|
||||
cCtx.String(flagConfig),
|
||||
cCtx.String(flagOverlayName),
|
||||
cmd.String(flagConfig),
|
||||
cmd.String(flagOverlayName),
|
||||
secrets,
|
||||
false,
|
||||
)
|
||||
@@ -73,7 +74,7 @@ func commandConfigDev(cCtx *cli.Context) error {
|
||||
|
||||
for _, v := range cfg.GetEnvironment() {
|
||||
value := escape(v.Value)
|
||||
if cCtx.Bool(flagDevPrependExport) {
|
||||
if cmd.Bool(flagDevPrependExport) {
|
||||
ce.Println("export %s=\"%s\"", v.Name, value)
|
||||
} else {
|
||||
ce.Println("%s=\"%s\"", v.Name, value)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package run
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
import "github.com/urfave/cli/v3"
|
||||
|
||||
func Command() *cli.Command {
|
||||
return &cli.Command{ //nolint:exhaustruct
|
||||
Name: "run",
|
||||
Aliases: []string{},
|
||||
Usage: "Perform operations on Nhost Run",
|
||||
Subcommands: []*cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
CommandConfigShow(),
|
||||
CommandConfigDeploy(),
|
||||
CommandConfigEdit(),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package secrets //nolint:dupl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandCreate() *cli.Command {
|
||||
@@ -19,28 +20,28 @@ func CommandCreate() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandCreate(cCtx *cli.Context) error {
|
||||
if cCtx.NArg() != 2 { //nolint:mnd
|
||||
func commandCreate(ctx context.Context, cmd *cli.Command) error {
|
||||
if cmd.NArg() != 2 { //nolint:mnd
|
||||
return errors.New("invalid number of arguments") //nolint:err113
|
||||
}
|
||||
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
|
||||
proj, err := ce.GetAppInfo(ctx, cmd.String(flagSubdomain))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app info: %w", err)
|
||||
}
|
||||
|
||||
cl, err := ce.GetNhostClient(cCtx.Context)
|
||||
cl, err := ce.GetNhostClient(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nhost client: %w", err)
|
||||
}
|
||||
|
||||
if _, err := cl.CreateSecret(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
proj.ID,
|
||||
cCtx.Args().Get(0),
|
||||
cCtx.Args().Get(1),
|
||||
cmd.Args().Get(0),
|
||||
cmd.Args().Get(1),
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to create secret: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package secrets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandDelete() *cli.Command {
|
||||
@@ -19,27 +20,27 @@ func CommandDelete() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandDelete(cCtx *cli.Context) error {
|
||||
if cCtx.NArg() != 1 {
|
||||
func commandDelete(ctx context.Context, cmd *cli.Command) error {
|
||||
if cmd.NArg() != 1 {
|
||||
return errors.New("invalid number of arguments") //nolint:err113
|
||||
}
|
||||
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
|
||||
proj, err := ce.GetAppInfo(ctx, cmd.String(flagSubdomain))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app info: %w", err)
|
||||
}
|
||||
|
||||
cl, err := ce.GetNhostClient(cCtx.Context)
|
||||
cl, err := ce.GetNhostClient(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nhost client: %w", err)
|
||||
}
|
||||
|
||||
if _, err := cl.DeleteSecret(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
proj.ID,
|
||||
cCtx.Args().Get(0),
|
||||
cmd.Args().Get(0),
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to delete secret: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package secrets
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandList() *cli.Command {
|
||||
@@ -17,21 +18,21 @@ func CommandList() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandList(cCtx *cli.Context) error {
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
func commandList(ctx context.Context, cmd *cli.Command) error {
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
|
||||
proj, err := ce.GetAppInfo(ctx, cmd.String(flagSubdomain))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app info: %w", err)
|
||||
}
|
||||
|
||||
cl, err := ce.GetNhostClient(cCtx.Context)
|
||||
cl, err := ce.GetNhostClient(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nhost client: %w", err)
|
||||
}
|
||||
|
||||
secrets, err := cl.GetSecrets(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
proj.ID,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package secrets
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
import "github.com/urfave/cli/v3"
|
||||
|
||||
const flagSubdomain = "subdomain"
|
||||
|
||||
@@ -9,7 +9,7 @@ func commonFlags() []cli.Flag {
|
||||
&cli.StringFlag{ //nolint:exhaustruct
|
||||
Name: flagSubdomain,
|
||||
Usage: "Project's subdomain to operate on, defaults to linked project",
|
||||
EnvVars: []string{"NHOST_SUBDOMAIN"},
|
||||
Sources: cli.EnvVars("NHOST_SUBDOMAIN"),
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ func Command() *cli.Command {
|
||||
Name: "secrets",
|
||||
Aliases: []string{},
|
||||
Usage: "Manage secrets",
|
||||
Subcommands: []*cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
CommandCreate(),
|
||||
CommandDelete(),
|
||||
CommandList(),
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package secrets //nolint:dupl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nhost/nhost/cli/clienv"
|
||||
"github.com/urfave/cli/v2"
|
||||
"github.com/urfave/cli/v3"
|
||||
)
|
||||
|
||||
func CommandUpdate() *cli.Command {
|
||||
@@ -19,28 +20,28 @@ func CommandUpdate() *cli.Command {
|
||||
}
|
||||
}
|
||||
|
||||
func commandUpdate(cCtx *cli.Context) error {
|
||||
if cCtx.NArg() != 2 { //nolint:mnd
|
||||
func commandUpdate(ctx context.Context, cmd *cli.Command) error {
|
||||
if cmd.NArg() != 2 { //nolint:mnd
|
||||
return errors.New("invalid number of arguments") //nolint:err113
|
||||
}
|
||||
|
||||
ce := clienv.FromCLI(cCtx)
|
||||
ce := clienv.FromCLI(cmd)
|
||||
|
||||
proj, err := ce.GetAppInfo(cCtx.Context, cCtx.String(flagSubdomain))
|
||||
proj, err := ce.GetAppInfo(ctx, cmd.String(flagSubdomain))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app info: %w", err)
|
||||
}
|
||||
|
||||
cl, err := ce.GetNhostClient(cCtx.Context)
|
||||
cl, err := ce.GetNhostClient(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get nhost client: %w", err)
|
||||
}
|
||||
|
||||
if _, err := cl.UpdateSecret(
|
||||
cCtx.Context,
|
||||
ctx,
|
||||
proj.ID,
|
||||
cCtx.Args().Get(0),
|
||||
cCtx.Args().Get(1),
|
||||
cmd.Args().Get(0),
|
||||
cmd.Args().Get(1),
|
||||
); err != nil {
|
||||
return fmt.Errorf("failed to update secret: %w", err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package software
|
||||
|
||||
import "github.com/urfave/cli/v2"
|
||||
import "github.com/urfave/cli/v3"
|
||||
|
||||
const (
|
||||
devVersion = "dev"
|
||||
@@ -11,7 +11,7 @@ func Command() *cli.Command {
|
||||
Name: "sw",
|
||||
Aliases: []string{},
|
||||
Usage: "Perform software management operations",
|
||||
Subcommands: []*cli.Command{
|
||||
Commands: []*cli.Command{
|
||||
CommandUninstall(),
|
||||
CommandUpgrade(),
|
||||
CommandVersion(),
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user