mirror of
https://github.com/hatchet-dev/hatchet.git
synced 2026-04-23 10:39:45 -05:00
feat: github app integration (#163)
* feat: github app integration * chore: proto * fix: migrate instead of push * fix: db migrate -> migrate * fix: migrate again * remove skip-generate * add back generate * setup pnpm
This commit is contained in:
@@ -33,7 +33,7 @@ jobs:
|
||||
|
||||
- name: Generate
|
||||
run: |
|
||||
go run github.com/steebchen/prisma-client-go db push --skip-generate
|
||||
go run github.com/steebchen/prisma-client-go migrate deploy
|
||||
task generate
|
||||
|
||||
- name: Check for diff
|
||||
@@ -80,11 +80,20 @@ jobs:
|
||||
with:
|
||||
version: "25.1"
|
||||
|
||||
- name: Install Task
|
||||
uses: arduino/setup-task@v1
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.21"
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Compose
|
||||
run: docker compose up -d
|
||||
|
||||
@@ -92,7 +101,9 @@ jobs:
|
||||
run: go mod download
|
||||
|
||||
- name: Generate
|
||||
run: go run github.com/steebchen/prisma-client-go db push
|
||||
run: |
|
||||
go run github.com/steebchen/prisma-client-go migrate deploy
|
||||
task generate
|
||||
|
||||
- name: Test
|
||||
run: go test -tags integration ./... -v -failfast
|
||||
@@ -122,6 +133,12 @@ jobs:
|
||||
with:
|
||||
go-version: "1.21"
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
run_install: false
|
||||
|
||||
- name: Compose
|
||||
run: docker compose up -d
|
||||
|
||||
@@ -130,7 +147,8 @@ jobs:
|
||||
|
||||
- name: Generate
|
||||
run: |
|
||||
go run github.com/steebchen/prisma-client-go db push
|
||||
go run github.com/steebchen/prisma-client-go migrate deploy
|
||||
task generate
|
||||
task generate-certs
|
||||
|
||||
- name: Prepare
|
||||
|
||||
@@ -9,3 +9,9 @@ app.dev.hatchet-tools.com {
|
||||
reverse_proxy localhost:5173
|
||||
}
|
||||
}
|
||||
|
||||
grpc.dev.hatchet-tools.com {
|
||||
tls internal
|
||||
|
||||
reverse_proxy h2c://127.0.0.1:7070
|
||||
}
|
||||
+9
-1
@@ -9,7 +9,7 @@ tasks:
|
||||
- sh ./hack/dev/run-go-with-env.sh run github.com/steebchen/prisma-client-go migrate dev
|
||||
seed-dev:
|
||||
cmds:
|
||||
- sh ./hack/dev/run-npx-with-env.sh prisma db push --force-reset --skip-generate
|
||||
- sh ./hack/dev/run-go-with-env.sh run github.com/steebchen/prisma-client-go migrate dev --skip-generate
|
||||
- SEED_DEVELOPMENT=true sh ./hack/dev/run-go-with-env.sh run ./cmd/hatchet-admin seed
|
||||
start-dev:
|
||||
deps:
|
||||
@@ -85,6 +85,14 @@ tasks:
|
||||
kill-query-engines:
|
||||
cmds:
|
||||
- ps -A | grep 'prisma-query-engine-darwin-arm64' | grep -v grep | awk '{print $1}' | xargs kill -9 $1
|
||||
kill-apis:
|
||||
cmds:
|
||||
- ps -A | grep 'cmd/hatchet-api' | grep -v grep | awk '{print $1}' | xargs kill -9 $1
|
||||
- ps -A | grep 'exe/hatchet-api' | grep -v grep | awk '{print $1}' | xargs kill -9 $1
|
||||
kill-engines:
|
||||
cmds:
|
||||
- ps -A | grep 'cmd/hatchet-engine' | grep -v grep | awk '{print $1}' | xargs kill -9 $1
|
||||
- ps -A | grep 'exe/hatchet-engine' | grep -v grep | awk '{print $1}' | xargs kill -9 $1
|
||||
prisma-studio:
|
||||
cmds:
|
||||
- sh ./hack/dev/run-npx-with-env.sh prisma studio
|
||||
|
||||
@@ -221,6 +221,9 @@ message OverridesData {
|
||||
|
||||
// the value to set
|
||||
string value = 3;
|
||||
|
||||
// the filename of the caller
|
||||
string callerFilename = 4;
|
||||
}
|
||||
|
||||
message OverridesDataResponse {}
|
||||
@@ -68,6 +68,8 @@ ReplayEventRequest:
|
||||
$ref: "./event.yaml#/ReplayEventRequest"
|
||||
Workflow:
|
||||
$ref: "./workflow.yaml#/Workflow"
|
||||
WorkflowDeploymentConfig:
|
||||
$ref: "./workflow.yaml#/WorkflowDeploymentConfig"
|
||||
WorkflowVersionMeta:
|
||||
$ref: "./workflow.yaml#/WorkflowVersionMeta"
|
||||
WorkflowVersion:
|
||||
@@ -120,3 +122,19 @@ RerunStepRunRequest:
|
||||
$ref: "./workflow_run.yaml#/RerunStepRunRequest"
|
||||
TriggerWorkflowRunRequest:
|
||||
$ref: "./workflow_run.yaml#/TriggerWorkflowRunRequest"
|
||||
LinkGithubRepositoryRequest:
|
||||
$ref: "./workflow.yaml#/LinkGithubRepositoryRequest"
|
||||
GithubBranch:
|
||||
$ref: "./github_app.yaml#/GithubBranch"
|
||||
GithubRepo:
|
||||
$ref: "./github_app.yaml#/GithubRepo"
|
||||
GithubAppInstallation:
|
||||
$ref: "./github_app.yaml#/GithubAppInstallation"
|
||||
ListGithubAppInstallationsResponse:
|
||||
$ref: "./github_app.yaml#/ListGithubAppInstallationsResponse"
|
||||
ListGithubReposResponse:
|
||||
$ref: "./github_app.yaml#/ListGithubReposResponse"
|
||||
ListGithubBranchesResponse:
|
||||
$ref: "./github_app.yaml#/ListGithubBranchesResponse"
|
||||
CreatePullRequestFromStepRun:
|
||||
$ref: "./workflow_run.yaml#/CreatePullRequestFromStepRun"
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
GithubBranch:
|
||||
type: object
|
||||
properties:
|
||||
branch_name:
|
||||
type: string
|
||||
is_default:
|
||||
type: boolean
|
||||
required:
|
||||
- branch_name
|
||||
- is_default
|
||||
|
||||
GithubRepo:
|
||||
type: object
|
||||
properties:
|
||||
repo_owner:
|
||||
type: string
|
||||
repo_name:
|
||||
type: string
|
||||
required:
|
||||
- repo_owner
|
||||
- repo_name
|
||||
|
||||
GithubAppInstallation:
|
||||
type: object
|
||||
properties:
|
||||
metadata:
|
||||
$ref: "./metadata.yaml#/APIResourceMeta"
|
||||
installation_settings_url:
|
||||
type: string
|
||||
account_name:
|
||||
type: string
|
||||
account_avatar_url:
|
||||
type: string
|
||||
required:
|
||||
- metadata
|
||||
- installation_settings_url
|
||||
- account_name
|
||||
- account_avatar_url
|
||||
|
||||
ListGithubAppInstallationsResponse:
|
||||
type: object
|
||||
properties:
|
||||
pagination:
|
||||
$ref: "./metadata.yaml#/PaginationResponse"
|
||||
rows:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/GithubAppInstallation"
|
||||
required:
|
||||
- pagination
|
||||
- rows
|
||||
|
||||
ListGithubReposResponse:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/GithubRepo"
|
||||
|
||||
ListGithubBranchesResponse:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/GithubBranch"
|
||||
@@ -24,10 +24,38 @@ Workflow:
|
||||
items:
|
||||
$ref: "#/Job"
|
||||
description: The jobs of the workflow.
|
||||
deployment:
|
||||
$ref: "#/WorkflowDeploymentConfig"
|
||||
required:
|
||||
- metadata
|
||||
- name
|
||||
type: object
|
||||
WorkflowDeploymentConfig:
|
||||
properties:
|
||||
metadata:
|
||||
$ref: "./metadata.yaml#/APIResourceMeta"
|
||||
gitRepoName:
|
||||
type: string
|
||||
description: The repository name.
|
||||
gitRepoOwner:
|
||||
type: string
|
||||
description: The repository owner.
|
||||
gitRepoBranch:
|
||||
type: string
|
||||
description: The repository branch.
|
||||
githubAppInstallation:
|
||||
$ref: "./_index.yaml#/GithubAppInstallation"
|
||||
description: The Github App installation.
|
||||
githubAppInstallationId:
|
||||
type: string
|
||||
format: uuid
|
||||
description: The id of the Github App installation.
|
||||
required:
|
||||
- metadata
|
||||
- gitRepoName
|
||||
- gitRepoOwner
|
||||
- gitRepoBranch
|
||||
- githubAppInstallationId
|
||||
|
||||
WorkflowTag:
|
||||
type: object
|
||||
@@ -205,3 +233,25 @@ Step:
|
||||
- jobId
|
||||
- action
|
||||
- nextId
|
||||
LinkGithubRepositoryRequest:
|
||||
type: object
|
||||
properties:
|
||||
installationId:
|
||||
type: string
|
||||
description: The repository name.
|
||||
minLength: 36
|
||||
maxLength: 36
|
||||
gitRepoName:
|
||||
type: string
|
||||
description: The repository name.
|
||||
gitRepoOwner:
|
||||
type: string
|
||||
description: The repository owner.
|
||||
gitRepoBranch:
|
||||
type: string
|
||||
description: The repository branch.
|
||||
required:
|
||||
- installationId
|
||||
- gitRepoName
|
||||
- gitRepoOwner
|
||||
- gitRepoBranch
|
||||
|
||||
@@ -227,3 +227,10 @@ TriggerWorkflowRunRequest:
|
||||
type: object
|
||||
required:
|
||||
- input
|
||||
|
||||
CreatePullRequestFromStepRun:
|
||||
properties:
|
||||
branchName:
|
||||
type: string
|
||||
required:
|
||||
- branchName
|
||||
|
||||
@@ -28,6 +28,14 @@ paths:
|
||||
$ref: "./paths/user/user.yaml#/oauth-start-google"
|
||||
/api/v1/users/google/callback:
|
||||
$ref: "./paths/user/user.yaml#/oauth-callback-google"
|
||||
/api/v1/users/github/start:
|
||||
$ref: "./paths/user/user.yaml#/oauth-start-github"
|
||||
/api/v1/users/github/callback:
|
||||
$ref: "./paths/user/user.yaml#/oauth-callback-github"
|
||||
/api/v1/github/webhook:
|
||||
$ref: "./paths/github-app/github-app.yaml#/globalWebhook"
|
||||
/api/v1/github/webhook/{webhook}:
|
||||
$ref: "./paths/github-app/github-app.yaml#/tenantWebhook"
|
||||
/api/v1/users/current:
|
||||
$ref: "./paths/user/user.yaml#/current"
|
||||
/api/v1/users/register:
|
||||
@@ -72,6 +80,10 @@ paths:
|
||||
$ref: "./paths/workflow/workflow.yaml#/triggerWorkflow"
|
||||
/api/v1/workflows/{workflow}/versions/definition:
|
||||
$ref: "./paths/workflow/workflow.yaml#/workflowVersionDefinition"
|
||||
/api/v1/workflows/{workflow}/link-github:
|
||||
$ref: "./paths/workflow/workflow.yaml#/linkGithub"
|
||||
/api/v1/step-runs/{step-run}/create-pr:
|
||||
$ref: "./paths/workflow/workflow.yaml#/createPullRequest"
|
||||
/api/v1/tenants/{tenant}/workflows/runs:
|
||||
$ref: "./paths/workflow/workflow.yaml#/workflowRuns"
|
||||
/api/v1/tenants/{tenant}/workflow-runs/{workflow-run}:
|
||||
@@ -84,3 +96,9 @@ paths:
|
||||
$ref: "./paths/worker/worker.yaml#/withTenant"
|
||||
/api/v1/workers/{worker}:
|
||||
$ref: "./paths/worker/worker.yaml#/withWorker"
|
||||
/api/v1/github-app/installations:
|
||||
$ref: "./paths/github-app/github-app.yaml#/installations"
|
||||
/api/v1/github-app/installations/{gh-installation}/repos:
|
||||
$ref: "./paths/github-app/github-app.yaml#/repos"
|
||||
/api/v1/github-app/installations/{gh-installation}/repos/{gh-repo-owner}/{gh-repo-name}/branches:
|
||||
$ref: "./paths/github-app/github-app.yaml#/branches"
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
globalWebhook:
|
||||
post:
|
||||
description: Github App global webhook
|
||||
operationId: github:update:global-webhook
|
||||
responses:
|
||||
"200":
|
||||
description: Successfully processed webhook
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: A malformed or bad request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Unauthorized
|
||||
"405":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Method not allowed
|
||||
security: []
|
||||
summary: Github app global webhook
|
||||
tags:
|
||||
- Github
|
||||
tenantWebhook:
|
||||
post:
|
||||
description: Github App tenant webhook
|
||||
operationId: github:update:tenant-webhook
|
||||
parameters:
|
||||
- description: The webhook id
|
||||
in: path
|
||||
name: webhook
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
minLength: 36
|
||||
maxLength: 36
|
||||
responses:
|
||||
"200":
|
||||
description: Successfully processed webhook
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: A malformed or bad request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Unauthorized
|
||||
"405":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Method not allowed
|
||||
security: []
|
||||
summary: Github app tenant webhook
|
||||
tags:
|
||||
- Github
|
||||
installations:
|
||||
get:
|
||||
description: List Github App installations
|
||||
operationId: github-app:list:installations
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/ListGithubAppInstallationsResponse"
|
||||
description: Successfully retrieved the installations
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: A malformed or bad request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Unauthorized
|
||||
"405":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Method not allowed
|
||||
security:
|
||||
- cookieAuth: []
|
||||
summary: List Github App installations
|
||||
tags:
|
||||
- Github
|
||||
repos:
|
||||
get:
|
||||
description: List Github App repositories
|
||||
operationId: github-app:list:repos
|
||||
x-resources: ["gh-installation"]
|
||||
parameters:
|
||||
- description: The installation id
|
||||
in: path
|
||||
name: gh-installation
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
minLength: 36
|
||||
maxLength: 36
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/ListGithubReposResponse"
|
||||
description: Successfully retrieved the repositories
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: A malformed or bad request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Unauthorized
|
||||
"405":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Method not allowed
|
||||
security:
|
||||
- cookieAuth: []
|
||||
summary: List Github App repositories
|
||||
tags:
|
||||
- Github
|
||||
branches:
|
||||
get:
|
||||
description: List Github App branches
|
||||
operationId: github-app:list:branches
|
||||
x-resources: ["gh-installation"]
|
||||
parameters:
|
||||
- description: The installation id
|
||||
in: path
|
||||
name: gh-installation
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
minLength: 36
|
||||
maxLength: 36
|
||||
- description: The repository owner
|
||||
in: path
|
||||
name: gh-repo-owner
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- description: The repository name
|
||||
in: path
|
||||
name: gh-repo-name
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/ListGithubBranchesResponse"
|
||||
description: Successfully retrieved the branches
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: A malformed or bad request
|
||||
"401":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Unauthorized
|
||||
"405":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Method not allowed
|
||||
security:
|
||||
- cookieAuth: []
|
||||
summary: List Github App branches
|
||||
tags:
|
||||
- Github
|
||||
@@ -111,7 +111,7 @@ register:
|
||||
oauth-start-google:
|
||||
get:
|
||||
description: Starts the OAuth flow
|
||||
operationId: user:update:oauth-start
|
||||
operationId: user:update:google-oauth-start
|
||||
responses:
|
||||
"302":
|
||||
description: Successfully started the OAuth flow
|
||||
@@ -126,7 +126,7 @@ oauth-start-google:
|
||||
oauth-callback-google:
|
||||
get:
|
||||
description: Completes the OAuth flow
|
||||
operationId: user:update:oauth-callback
|
||||
operationId: user:update:google-oauth-callback
|
||||
responses:
|
||||
"302":
|
||||
description: Successfully completed the OAuth flow
|
||||
@@ -138,6 +138,42 @@ oauth-callback-google:
|
||||
summary: Complete OAuth flow
|
||||
tags:
|
||||
- User
|
||||
oauth-start-github:
|
||||
get:
|
||||
description: Starts the OAuth flow
|
||||
operationId: user:update:github-oauth-start
|
||||
responses:
|
||||
"302":
|
||||
description: Successfully started the OAuth flow
|
||||
headers:
|
||||
location:
|
||||
schema:
|
||||
type: string
|
||||
# Note that the security scheme requires cookies, because this endpoint is for linking
|
||||
# a GitHub account to an existing user account.
|
||||
security:
|
||||
- cookieAuth: []
|
||||
summary: Start OAuth flow
|
||||
tags:
|
||||
- User
|
||||
oauth-callback-github:
|
||||
get:
|
||||
description: Completes the OAuth flow
|
||||
operationId: user:update:github-oauth-callback
|
||||
responses:
|
||||
"302":
|
||||
description: Successfully completed the OAuth flow
|
||||
headers:
|
||||
location:
|
||||
schema:
|
||||
type: string
|
||||
# Note that the security scheme requires cookies, because this endpoint is for linking
|
||||
# a GitHub account to an existing user account.
|
||||
security:
|
||||
- cookieAuth: []
|
||||
summary: Complete OAuth flow
|
||||
tags:
|
||||
- User
|
||||
logout:
|
||||
post:
|
||||
description: Logs out a user.
|
||||
|
||||
@@ -350,3 +350,103 @@ workflowRun:
|
||||
summary: Get workflow run
|
||||
tags:
|
||||
- Workflow
|
||||
linkGithub:
|
||||
post:
|
||||
x-resources: ["tenant", "workflow"]
|
||||
description: Link a github repository to a workflow
|
||||
operationId: workflow:update:link-github
|
||||
parameters:
|
||||
- description: The workflow id
|
||||
in: path
|
||||
name: workflow
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
minLength: 36
|
||||
maxLength: 36
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/LinkGithubRepositoryRequest"
|
||||
description: The input to link a github repository
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/Workflow"
|
||||
description: Successfully linked the github repository
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: A malformed or bad request
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Forbidden
|
||||
"404":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Not found
|
||||
summary: Link github repository
|
||||
tags:
|
||||
- Workflow
|
||||
createPullRequest:
|
||||
post:
|
||||
x-resources: ["tenant", "step-run"]
|
||||
description: Create a pull request for a workflow
|
||||
operationId: step-run:update:create-pr
|
||||
parameters:
|
||||
- description: The step run id
|
||||
in: path
|
||||
name: step-run
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
minLength: 36
|
||||
maxLength: 36
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/CreatePullRequestFromStepRun"
|
||||
description: The input to create a pull request
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/CreatePullRequestFromStepRun"
|
||||
description: Successfully created the pull request
|
||||
"400":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: A malformed or bad request
|
||||
"403":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Forbidden
|
||||
"404":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "../../components/schemas/_index.yaml#/APIErrors"
|
||||
description: Not found
|
||||
summary: Create pull request
|
||||
tags:
|
||||
- Workflow
|
||||
|
||||
@@ -55,6 +55,7 @@ func (s *SessionHelpers) SaveUnauthenticated(c echo.Context) error {
|
||||
|
||||
func (s *SessionHelpers) SaveOAuthState(
|
||||
c echo.Context,
|
||||
integration string,
|
||||
) (string, error) {
|
||||
state, err := encryption.GenerateRandomBytes(16)
|
||||
|
||||
@@ -68,8 +69,10 @@ func (s *SessionHelpers) SaveOAuthState(
|
||||
return "", err
|
||||
}
|
||||
|
||||
stateKey := fmt.Sprintf("oauth_state_%s", integration)
|
||||
|
||||
// need state parameter to validate when redirected
|
||||
session.Values["state"] = state
|
||||
session.Values[stateKey] = state
|
||||
|
||||
// need a parameter to indicate that this was triggered through the oauth flow
|
||||
session.Values["oauth_triggered"] = true
|
||||
@@ -83,18 +86,21 @@ func (s *SessionHelpers) SaveOAuthState(
|
||||
|
||||
func (s *SessionHelpers) ValidateOAuthState(
|
||||
c echo.Context,
|
||||
integration string,
|
||||
) (isValidated bool, isOAuthTriggered bool, err error) {
|
||||
stateKey := fmt.Sprintf("oauth_state_%s", integration)
|
||||
|
||||
session, err := s.config.SessionStore.Get(c.Request(), s.config.SessionStore.GetName())
|
||||
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
|
||||
if _, ok := session.Values["state"]; !ok {
|
||||
if _, ok := session.Values[stateKey]; !ok {
|
||||
return false, false, fmt.Errorf("state parameter not found in session")
|
||||
}
|
||||
|
||||
if c.Request().URL.Query().Get("state") != session.Values["state"] {
|
||||
if c.Request().URL.Query().Get("state") != session.Values[stateKey] {
|
||||
return false, false, fmt.Errorf("state parameters do not match")
|
||||
}
|
||||
|
||||
@@ -107,7 +113,7 @@ func (s *SessionHelpers) ValidateOAuthState(
|
||||
}
|
||||
|
||||
// need state parameter to validate when redirected
|
||||
session.Values["state"] = ""
|
||||
session.Values[stateKey] = ""
|
||||
session.Values["oauth_triggered"] = false
|
||||
|
||||
if err := session.Save(c.Request(), c.Response()); err != nil {
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package githubapp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
"github.com/hatchet-dev/hatchet/internal/integrations/vcs/github"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository"
|
||||
|
||||
githubsdk "github.com/google/go-github/v57/github"
|
||||
)
|
||||
|
||||
// Note: we want all errors to redirect, otherwise the user will be greeted with raw JSON in the middle of the login flow.
|
||||
func (g *GithubAppService) GithubUpdateTenantWebhook(ctx echo.Context, req gen.GithubUpdateTenantWebhookRequestObject) (gen.GithubUpdateTenantWebhookResponseObject, error) {
|
||||
webhookId := req.Webhook.String()
|
||||
|
||||
webhook, err := g.config.Repository.Github().ReadGithubWebhookById(webhookId)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signingSecret, err := g.config.Encryption.Decrypt(webhook.SigningSecret, "github_signing_secret")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// validate the payload using the github webhook signing secret
|
||||
payload, err := githubsdk.ValidatePayload(ctx.Request(), signingSecret)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
event, err := githubsdk.ParseWebHook(githubsdk.WebHookType(ctx.Request()), payload)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch event := event.(type) { // nolint: gocritic
|
||||
case *githubsdk.PullRequestEvent:
|
||||
err = g.processPullRequestEvent(webhook.TenantID, event, ctx.Request())
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (g *GithubAppService) processPullRequestEvent(tenantId string, event *githubsdk.PullRequestEvent, r *http.Request) error {
|
||||
pr := github.ToVCSRepositoryPullRequest(*event.GetRepo().GetOwner().Login, event.GetRepo().GetName(), event.GetPullRequest())
|
||||
|
||||
dbPR, err := g.config.Repository.Github().GetPullRequest(tenantId, pr.GetRepoOwner(), pr.GetRepoName(), int(pr.GetPRNumber()))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = g.config.Repository.Github().UpdatePullRequest(tenantId, dbPR.ID, &repository.UpdatePullRequestOpts{
|
||||
HeadBranch: repository.StringPtr(pr.GetHeadBranch()),
|
||||
BaseBranch: repository.StringPtr(pr.GetBaseBranch()),
|
||||
Title: repository.StringPtr(pr.GetTitle()),
|
||||
State: repository.StringPtr(pr.GetState()),
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package githubapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/apierrors"
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
"github.com/hatchet-dev/hatchet/internal/config/server"
|
||||
"github.com/hatchet-dev/hatchet/internal/integrations/vcs"
|
||||
"github.com/hatchet-dev/hatchet/internal/integrations/vcs/github"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
|
||||
githubsdk "github.com/google/go-github/v57/github"
|
||||
)
|
||||
|
||||
func GetGithubProvider(config *server.ServerConfig) (res github.GithubVCSProvider, reqErr error) {
|
||||
vcsProvider, exists := config.VCSProviders[vcs.VCSRepositoryKindGithub]
|
||||
|
||||
if !exists {
|
||||
return res, fmt.Errorf("No Github app set up on this Hatchet instance.")
|
||||
}
|
||||
|
||||
res, err := github.ToGithubVCSProvider(vcsProvider)
|
||||
|
||||
if err != nil {
|
||||
return res, fmt.Errorf("Github app is improperly set up on this Hatchet instance.")
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func GetGithubAppConfig(config *server.ServerConfig) (*github.GithubAppConf, error) {
|
||||
githubFact, reqErr := GetGithubProvider(config)
|
||||
|
||||
if reqErr != nil {
|
||||
return nil, reqErr
|
||||
}
|
||||
|
||||
return githubFact.GetGithubAppConfig(), nil
|
||||
}
|
||||
|
||||
// GetGithubAppClientFromRequest gets the github app installation id from the request and authenticates
|
||||
// using it and the private key
|
||||
func GetGithubAppClientFromRequest(ctx echo.Context, config *server.ServerConfig) (*githubsdk.Client, *gen.APIErrors) {
|
||||
user := ctx.Get("user").(*db.UserModel)
|
||||
gai := ctx.Get("gh-installation").(*db.GithubAppInstallationModel)
|
||||
|
||||
if canAccess, err := config.Repository.Github().CanUserAccessInstallation(gai.ID, user.ID); err != nil || !canAccess {
|
||||
respErr := apierrors.NewAPIErrors("User does not have access to the installation")
|
||||
return nil, &respErr
|
||||
}
|
||||
|
||||
githubFact, err := GetGithubProvider(config)
|
||||
|
||||
if err != nil {
|
||||
config.Logger.Err(err).Msg("Error getting github provider")
|
||||
respErr := apierrors.NewAPIErrors("Internal error")
|
||||
return nil, &respErr
|
||||
}
|
||||
|
||||
res, err := githubFact.GetGithubAppConfig().GetGithubClient(int64(gai.InstallationID))
|
||||
|
||||
if err != nil {
|
||||
config.Logger.Err(err).Msg("Error getting github client")
|
||||
respErr := apierrors.NewAPIErrors("Internal error")
|
||||
return nil, &respErr
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package githubapp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
|
||||
githubsdk "github.com/google/go-github/v57/github"
|
||||
)
|
||||
|
||||
func (g *GithubAppService) GithubAppListBranches(ctx echo.Context, req gen.GithubAppListBranchesRequestObject) (gen.GithubAppListBranchesResponseObject, error) {
|
||||
owner := req.GhRepoOwner
|
||||
name := req.GhRepoName
|
||||
|
||||
client, reqErr := GetGithubAppClientFromRequest(ctx, g.config)
|
||||
|
||||
if reqErr != nil {
|
||||
return gen.GithubAppListBranches400JSONResponse(
|
||||
*reqErr,
|
||||
), nil
|
||||
}
|
||||
|
||||
repo, _, err := client.Repositories.Get(
|
||||
context.TODO(),
|
||||
owner,
|
||||
name,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defaultBranch := repo.GetDefaultBranch()
|
||||
|
||||
// List all branches for a specified repo
|
||||
allBranches, resp, err := client.Repositories.ListBranches(context.Background(), owner, name, &githubsdk.BranchListOptions{
|
||||
ListOptions: githubsdk.ListOptions{
|
||||
PerPage: 100,
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// make workers to get branches concurrently
|
||||
const WCOUNT = 5
|
||||
numPages := resp.LastPage + 1
|
||||
var workerErr error
|
||||
var mu sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
|
||||
worker := func(cp int) {
|
||||
defer wg.Done()
|
||||
|
||||
for cp < numPages {
|
||||
opts := &githubsdk.BranchListOptions{
|
||||
ListOptions: githubsdk.ListOptions{
|
||||
Page: cp,
|
||||
PerPage: 100,
|
||||
},
|
||||
}
|
||||
|
||||
branches, _, err := client.Repositories.ListBranches(context.Background(), owner, name, opts)
|
||||
|
||||
if err != nil {
|
||||
mu.Lock()
|
||||
workerErr = err
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
allBranches = append(allBranches, branches...)
|
||||
mu.Unlock()
|
||||
|
||||
cp += WCOUNT
|
||||
}
|
||||
}
|
||||
|
||||
var numJobs int
|
||||
if numPages > WCOUNT {
|
||||
numJobs = WCOUNT
|
||||
} else {
|
||||
numJobs = numPages
|
||||
}
|
||||
|
||||
wg.Add(numJobs)
|
||||
|
||||
// page 1 is already loaded so we start with 2
|
||||
for i := 1; i <= numJobs; i++ {
|
||||
go worker(i + 1)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if workerErr != nil {
|
||||
return nil, workerErr
|
||||
}
|
||||
|
||||
res := make([]gen.GithubBranch, 0)
|
||||
|
||||
for _, branch := range allBranches {
|
||||
res = append(res, gen.GithubBranch{
|
||||
BranchName: *branch.Name,
|
||||
IsDefault: defaultBranch == *branch.Name,
|
||||
})
|
||||
}
|
||||
|
||||
return gen.GithubAppListBranches200JSONResponse(
|
||||
res,
|
||||
), nil
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package githubapp
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/transformers"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
)
|
||||
|
||||
func (g *GithubAppService) GithubAppListInstallations(ctx echo.Context, req gen.GithubAppListInstallationsRequestObject) (gen.GithubAppListInstallationsResponseObject, error) {
|
||||
user := ctx.Get("user").(*db.UserModel)
|
||||
|
||||
gais, err := g.config.Repository.Github().ListGithubAppInstallationsByUserID(user.ID)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows := make([]gen.GithubAppInstallation, 0)
|
||||
|
||||
for i := range gais {
|
||||
rows = append(rows, *transformers.ToInstallation(&gais[i]))
|
||||
}
|
||||
|
||||
return gen.GithubAppListInstallations200JSONResponse(
|
||||
gen.ListGithubAppInstallationsResponse{
|
||||
Rows: rows,
|
||||
},
|
||||
), nil
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package githubapp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
|
||||
githubsdk "github.com/google/go-github/v57/github"
|
||||
)
|
||||
|
||||
func (g *GithubAppService) GithubAppListRepos(ctx echo.Context, req gen.GithubAppListReposRequestObject) (gen.GithubAppListReposResponseObject, error) {
|
||||
client, reqErr := GetGithubAppClientFromRequest(ctx, g.config)
|
||||
|
||||
if reqErr != nil {
|
||||
return gen.GithubAppListRepos400JSONResponse(
|
||||
*reqErr,
|
||||
), nil
|
||||
}
|
||||
|
||||
// figure out number of repositories
|
||||
opt := &githubsdk.ListOptions{
|
||||
PerPage: 100,
|
||||
}
|
||||
|
||||
repoList, resp, err := client.Apps.ListRepos(context.Background(), opt)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allRepos := repoList.Repositories
|
||||
|
||||
// make workers to get pages concurrently
|
||||
const WCOUNT = 5
|
||||
numPages := resp.LastPage + 1
|
||||
var workerErr error
|
||||
var mu sync.Mutex
|
||||
var wg sync.WaitGroup
|
||||
|
||||
worker := func(cp int) {
|
||||
defer wg.Done()
|
||||
|
||||
for cp < numPages {
|
||||
cur_opt := &githubsdk.ListOptions{
|
||||
Page: cp,
|
||||
PerPage: 100,
|
||||
}
|
||||
|
||||
repos, _, err := client.Apps.ListRepos(context.Background(), cur_opt)
|
||||
|
||||
if err != nil {
|
||||
mu.Lock()
|
||||
workerErr = err
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
allRepos = append(allRepos, repos.Repositories...)
|
||||
mu.Unlock()
|
||||
|
||||
cp += WCOUNT
|
||||
}
|
||||
}
|
||||
|
||||
var numJobs int
|
||||
if numPages > WCOUNT {
|
||||
numJobs = WCOUNT
|
||||
} else {
|
||||
numJobs = numPages
|
||||
}
|
||||
|
||||
wg.Add(numJobs)
|
||||
|
||||
// page 1 is already loaded so we start with 2
|
||||
for i := 1; i <= numJobs; i++ {
|
||||
go worker(i + 1)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if workerErr != nil {
|
||||
return nil, workerErr
|
||||
}
|
||||
|
||||
res := make([]gen.GithubRepo, 0)
|
||||
|
||||
for _, repo := range allRepos {
|
||||
res = append(res, gen.GithubRepo{
|
||||
RepoName: *repo.Name,
|
||||
RepoOwner: *repo.Owner.Login,
|
||||
})
|
||||
}
|
||||
|
||||
return gen.GithubAppListRepos200JSONResponse(
|
||||
res,
|
||||
), nil
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package githubapp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/google/go-github/v57/github"
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/authn"
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
)
|
||||
|
||||
// Note: we want all errors to redirect, otherwise the user will be greeted with raw JSON in the middle of the login flow.
|
||||
func (g *GithubAppService) UserUpdateGithubOauthCallback(ctx echo.Context, _ gen.UserUpdateGithubOauthCallbackRequestObject) (gen.UserUpdateGithubOauthCallbackResponseObject, error) {
|
||||
user := ctx.Get("user").(*db.UserModel)
|
||||
|
||||
ghApp, err := GetGithubAppConfig(g.config)
|
||||
|
||||
if err != nil {
|
||||
return nil, authn.GetRedirectWithError(ctx, g.config.Logger, err, "Github app is misconfigured on this Hatchet instance.")
|
||||
}
|
||||
|
||||
isValid, isOAuthTriggered, err := authn.NewSessionHelpers(g.config).ValidateOAuthState(ctx, "github")
|
||||
|
||||
if err != nil || !isValid {
|
||||
return nil, authn.GetRedirectWithError(ctx, g.config.Logger, err, "Could not link Github account. Please try again and make sure cookies are enabled.")
|
||||
}
|
||||
|
||||
token, err := ghApp.Exchange(context.Background(), ctx.Request().URL.Query().Get("code"))
|
||||
|
||||
if err != nil {
|
||||
return nil, authn.GetRedirectWithError(ctx, g.config.Logger, err, "Forbidden")
|
||||
}
|
||||
|
||||
if !token.Valid() {
|
||||
return nil, authn.GetRedirectWithError(ctx, g.config.Logger, fmt.Errorf("invalid token"), "Forbidden")
|
||||
}
|
||||
|
||||
ghClient := github.NewClient(ghApp.Client(context.Background(), token))
|
||||
|
||||
githubUser, _, err := ghClient.Users.Get(context.Background(), "")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if githubUser.ID == nil {
|
||||
return nil, authn.GetRedirectWithError(ctx, g.config.Logger, err, "Could not get Github user ID.")
|
||||
}
|
||||
|
||||
expiresAt := token.Expiry
|
||||
|
||||
// use the encryption service to encrypt the access and refresh token
|
||||
accessTokenEncrypted, err := g.config.Encryption.Encrypt([]byte(token.AccessToken), "github_access_token")
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encrypt access token: %s", err.Error())
|
||||
}
|
||||
|
||||
refreshTokenEncrypted, err := g.config.Encryption.Encrypt([]byte(token.RefreshToken), "github_refresh_token")
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encrypt refresh token: %s", err.Error())
|
||||
}
|
||||
|
||||
// upsert in database
|
||||
_, err = g.config.Repository.Github().UpsertGithubAppOAuth(user.ID, &repository.CreateGithubAppOAuthOpts{
|
||||
GithubUserID: int(*githubUser.ID),
|
||||
AccessToken: accessTokenEncrypted,
|
||||
RefreshToken: &refreshTokenEncrypted,
|
||||
ExpiresAt: &expiresAt,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, authn.GetRedirectWithError(ctx, g.config.Logger, err, "Internal error.")
|
||||
}
|
||||
|
||||
var url string
|
||||
|
||||
if isOAuthTriggered {
|
||||
url = fmt.Sprintf("https://github.com/apps/%s/installations/new", ghApp.GetAppName())
|
||||
} else {
|
||||
url = "/"
|
||||
}
|
||||
|
||||
return gen.UserUpdateGithubOauthCallback302Response{
|
||||
Headers: gen.UserUpdateGithubOauthCallback302ResponseHeaders{
|
||||
Location: url,
|
||||
},
|
||||
}, nil
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package githubapp
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/authn"
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
)
|
||||
|
||||
// Note: we want all errors to redirect, otherwise the user will be greeted with raw JSON in the middle of the login flow.
|
||||
func (g *GithubAppService) UserUpdateGithubOauthStart(ctx echo.Context, _ gen.UserUpdateGithubOauthStartRequestObject) (gen.UserUpdateGithubOauthStartResponseObject, error) {
|
||||
ghApp, err := GetGithubAppConfig(g.config)
|
||||
|
||||
if err != nil {
|
||||
return nil, authn.GetRedirectWithError(ctx, g.config.Logger, err, "Github app is misconfigured on this Hatchet instance.")
|
||||
}
|
||||
|
||||
state, err := authn.NewSessionHelpers(g.config).SaveOAuthState(ctx, "github")
|
||||
|
||||
if err != nil {
|
||||
return nil, authn.GetRedirectWithError(ctx, g.config.Logger, err, "Could not get cookie. Please make sure cookies are enabled.")
|
||||
}
|
||||
|
||||
url := ghApp.AuthCodeURL(state, oauth2.AccessTypeOffline)
|
||||
|
||||
return gen.UserUpdateGithubOauthStart302Response{
|
||||
Headers: gen.UserUpdateGithubOauthStart302ResponseHeaders{
|
||||
Location: url,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package githubapp
|
||||
|
||||
import "github.com/hatchet-dev/hatchet/internal/config/server"
|
||||
|
||||
type GithubAppService struct {
|
||||
config *server.ServerConfig
|
||||
}
|
||||
|
||||
func NewGithubAppService(config *server.ServerConfig) *GithubAppService {
|
||||
return &GithubAppService{
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package githubapp
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
|
||||
githubsdk "github.com/google/go-github/v57/github"
|
||||
)
|
||||
|
||||
// Note: we want all errors to redirect, otherwise the user will be greeted with raw JSON in the middle of the login flow.
|
||||
func (g *GithubAppService) GithubUpdateGlobalWebhook(ctx echo.Context, req gen.GithubUpdateGlobalWebhookRequestObject) (gen.GithubUpdateGlobalWebhookResponseObject, error) {
|
||||
ghApp, err := GetGithubAppConfig(g.config)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// validate the payload using the github webhook signing secret
|
||||
payload, err := githubsdk.ValidatePayload(ctx.Request(), []byte(ghApp.GetWebhookSecret()))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
event, err := githubsdk.ParseWebHook(githubsdk.WebHookType(ctx.Request()), payload)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch e := event.(type) {
|
||||
case *githubsdk.InstallationRepositoriesEvent:
|
||||
if *e.Action == "added" {
|
||||
err = g.handleInstallationEvent(*e.Sender.ID, e.Installation)
|
||||
}
|
||||
case *githubsdk.InstallationEvent:
|
||||
if *e.Action == "created" || *e.Action == "added" {
|
||||
err = g.handleInstallationEvent(*e.Sender.ID, e.Installation)
|
||||
}
|
||||
|
||||
if *e.Action == "deleted" {
|
||||
err = g.handleDeletionEvent(e.Installation)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (g *GithubAppService) handleInstallationEvent(senderID int64, i *githubsdk.Installation) error {
|
||||
// make sure the sender exists in the database
|
||||
gao, err := g.config.Repository.Github().ReadGithubAppOAuthByGithubUserID(int(senderID))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = g.config.Repository.Github().ReadGithubAppInstallationByInstallationAndAccountID(int(*i.ID), int(*i.Account.ID))
|
||||
|
||||
if err != nil && errors.Is(err, db.ErrNotFound) {
|
||||
// insert account/installation pair into database
|
||||
_, err := g.config.Repository.Github().CreateInstallation(gao.GithubUserID, &repository.CreateInstallationOpts{
|
||||
InstallationID: int(*i.ID),
|
||||
AccountID: int(*i.Account.ID),
|
||||
AccountName: *i.Account.Login,
|
||||
AccountAvatarURL: i.Account.AvatarURL,
|
||||
InstallationSettingsURL: i.HTMLURL,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// associate the github user id with this installation in the database
|
||||
_, err = g.config.Repository.Github().AddGithubUserIdToInstallation(int(*i.ID), int(*i.Account.ID), gao.GithubUserID)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (g *GithubAppService) handleDeletionEvent(i *githubsdk.Installation) error {
|
||||
_, err := g.config.Repository.Github().ReadGithubAppInstallationByInstallationAndAccountID(int(*i.ID), int(*i.Account.ID))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = g.config.Repository.Github().DeleteInstallation(int(*i.ID), int(*i.Account.ID))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -37,6 +37,12 @@ func (t *StepRunService) StepRunUpdateRerun(ctx echo.Context, request gen.StepRu
|
||||
), nil
|
||||
}
|
||||
|
||||
err = t.config.Repository.StepRun().ArchiveStepRunResult(tenant.ID, stepRun.ID)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not archive step run result: %w", err)
|
||||
}
|
||||
|
||||
// make sure input can be marshalled and unmarshalled to input type
|
||||
inputBytes, err := json.Marshal(request.Body.Input)
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ import (
|
||||
)
|
||||
|
||||
// Note: we want all errors to redirect, otherwise the user will be greeted with raw JSON in the middle of the login flow.
|
||||
func (u *UserService) UserUpdateOauthCallback(ctx echo.Context, _ gen.UserUpdateOauthCallbackRequestObject) (gen.UserUpdateOauthCallbackResponseObject, error) {
|
||||
isValid, _, err := authn.NewSessionHelpers(u.config).ValidateOAuthState(ctx)
|
||||
func (u *UserService) UserUpdateGoogleOauthCallback(ctx echo.Context, _ gen.UserUpdateGoogleOauthCallbackRequestObject) (gen.UserUpdateGoogleOauthCallbackResponseObject, error) {
|
||||
isValid, _, err := authn.NewSessionHelpers(u.config).ValidateOAuthState(ctx, "google")
|
||||
|
||||
if err != nil || !isValid {
|
||||
return nil, authn.GetRedirectWithError(ctx, u.config.Logger, err, "Could not log in. Please try again and make sure cookies are enabled.")
|
||||
@@ -47,8 +47,8 @@ func (u *UserService) UserUpdateOauthCallback(ctx echo.Context, _ gen.UserUpdate
|
||||
return nil, authn.GetRedirectWithError(ctx, u.config.Logger, err, "Internal error.")
|
||||
}
|
||||
|
||||
return gen.UserUpdateOauthCallback302Response{
|
||||
Headers: gen.UserUpdateOauthCallback302ResponseHeaders{
|
||||
return gen.UserUpdateGoogleOauthCallback302Response{
|
||||
Headers: gen.UserUpdateGoogleOauthCallback302ResponseHeaders{
|
||||
Location: u.config.Runtime.ServerURL,
|
||||
},
|
||||
}, nil
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
)
|
||||
|
||||
// Note: we want all errors to redirect, otherwise the user will be greeted with raw JSON in the middle of the login flow.
|
||||
func (u *UserService) UserUpdateOauthStart(ctx echo.Context, _ gen.UserUpdateOauthStartRequestObject) (gen.UserUpdateOauthStartResponseObject, error) {
|
||||
state, err := authn.NewSessionHelpers(u.config).SaveOAuthState(ctx)
|
||||
func (u *UserService) UserUpdateGoogleOauthStart(ctx echo.Context, _ gen.UserUpdateGoogleOauthStartRequestObject) (gen.UserUpdateGoogleOauthStartResponseObject, error) {
|
||||
state, err := authn.NewSessionHelpers(u.config).SaveOAuthState(ctx, "google")
|
||||
|
||||
if err != nil {
|
||||
return nil, authn.GetRedirectWithError(ctx, u.config.Logger, err, "Could not get cookie. Please make sure cookies are enabled.")
|
||||
@@ -17,8 +17,8 @@ func (u *UserService) UserUpdateOauthStart(ctx echo.Context, _ gen.UserUpdateOau
|
||||
|
||||
url := u.config.Auth.GoogleOAuthConfig.AuthCodeURL(state)
|
||||
|
||||
return gen.UserUpdateOauthStart302Response{
|
||||
Headers: gen.UserUpdateOauthStart302ResponseHeaders{
|
||||
return gen.UserUpdateGoogleOauthStart302Response{
|
||||
Headers: gen.UserUpdateGoogleOauthStart302ResponseHeaders{
|
||||
Location: url,
|
||||
},
|
||||
}, nil
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package workflows
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
"github.com/hatchet-dev/hatchet/internal/services/worker"
|
||||
)
|
||||
|
||||
func (t *WorkflowService) StepRunUpdateCreatePr(ctx echo.Context, request gen.StepRunUpdateCreatePrRequestObject) (gen.StepRunUpdateCreatePrResponseObject, error) {
|
||||
stepRun := ctx.Get("step-run").(*db.StepRunModel)
|
||||
|
||||
// trigger the workflow run
|
||||
_, err := t.config.InternalClient.Admin().RunWorkflow(worker.PullRequestWorkflow, &worker.StartPullRequestEvent{
|
||||
TenantID: stepRun.TenantID,
|
||||
StepRunID: stepRun.ID,
|
||||
BranchName: request.Body.BranchName,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("not implemented")
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package workflows
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/apierrors"
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/transformers"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
)
|
||||
|
||||
func (t *WorkflowService) WorkflowUpdateLinkGithub(ctx echo.Context, request gen.WorkflowUpdateLinkGithubRequestObject) (gen.WorkflowUpdateLinkGithubResponseObject, error) {
|
||||
user := ctx.Get("user").(*db.UserModel)
|
||||
workflow := ctx.Get("workflow").(*db.WorkflowModel)
|
||||
|
||||
// check that the user has access to the installation id
|
||||
installationId := request.Body.InstallationId
|
||||
|
||||
_, err := t.config.Repository.Github().ReadGithubAppInstallationByID(installationId)
|
||||
|
||||
if err != nil {
|
||||
return gen.WorkflowUpdateLinkGithub404JSONResponse(
|
||||
apierrors.NewAPIErrors("Installation not found"),
|
||||
), nil
|
||||
}
|
||||
|
||||
if canAccess, err := t.config.Repository.Github().CanUserAccessInstallation(installationId, user.ID); err != nil || !canAccess {
|
||||
return gen.WorkflowUpdateLinkGithub403JSONResponse(
|
||||
apierrors.NewAPIErrors("User does not have access to the installation"),
|
||||
), nil
|
||||
}
|
||||
|
||||
_, err = t.config.Repository.Workflow().UpsertWorkflowDeploymentConfig(
|
||||
workflow.ID,
|
||||
&repository.UpsertWorkflowDeploymentConfigOpts{
|
||||
GithubAppInstallationId: installationId,
|
||||
GitRepoName: request.Body.GitRepoName,
|
||||
GitRepoOwner: request.Body.GitRepoOwner,
|
||||
GitRepoBranch: request.Body.GitRepoBranch,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
workflow, err = t.config.Repository.Workflow().GetWorkflowById(workflow.ID)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := transformers.ToWorkflow(workflow, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return gen.WorkflowUpdateLinkGithub200JSONResponse(*resp), nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
package transformers
|
||||
|
||||
import (
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
)
|
||||
|
||||
func ToInstallation(gai *db.GithubAppInstallationModel) *gen.GithubAppInstallation {
|
||||
res := &gen.GithubAppInstallation{
|
||||
Metadata: *toAPIMetadata(gai.ID, gai.CreatedAt, gai.UpdatedAt),
|
||||
AccountName: gai.AccountName,
|
||||
}
|
||||
|
||||
if settingsUrl, ok := gai.InstallationSettingsURL(); ok {
|
||||
res.InstallationSettingsUrl = settingsUrl
|
||||
}
|
||||
|
||||
if avatarURL, ok := gai.AccountAvatarURL(); ok {
|
||||
res.AccountAvatarUrl = avatarURL
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
@@ -3,6 +3,8 @@ package transformers
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
@@ -45,6 +47,18 @@ func ToWorkflow(workflow *db.WorkflowModel, lastRun *db.WorkflowRunModel) (*gen.
|
||||
}
|
||||
}
|
||||
|
||||
if workflow.RelationsWorkflow.DeploymentConfig != nil {
|
||||
if deploymentConfig, ok := workflow.DeploymentConfig(); ok && deploymentConfig != nil {
|
||||
apiDeploymentConfig, err := ToWorkflowDeploymentConfig(deploymentConfig)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res.Deployment = apiDeploymentConfig
|
||||
}
|
||||
}
|
||||
|
||||
if workflow.RelationsWorkflow.Versions != nil {
|
||||
if versions := workflow.Versions(); versions != nil {
|
||||
apiVersions := make([]gen.WorkflowVersionMeta, len(versions))
|
||||
@@ -75,6 +89,26 @@ func ToWorkflowVersionMeta(version *db.WorkflowVersionModel) *gen.WorkflowVersio
|
||||
return res
|
||||
}
|
||||
|
||||
func ToWorkflowDeploymentConfig(deploymentConfig *db.WorkflowDeploymentConfigModel) (*gen.WorkflowDeploymentConfig, error) {
|
||||
res := &gen.WorkflowDeploymentConfig{
|
||||
Metadata: *toAPIMetadata(deploymentConfig.ID, deploymentConfig.CreatedAt, deploymentConfig.UpdatedAt),
|
||||
GitRepoName: deploymentConfig.GitRepoName,
|
||||
GitRepoOwner: deploymentConfig.GitRepoOwner,
|
||||
GitRepoBranch: deploymentConfig.GitRepoBranch,
|
||||
}
|
||||
|
||||
if githubAppInstallationId, ok := deploymentConfig.GithubAppInstallationID(); ok {
|
||||
res.GithubAppInstallationId = uuid.MustParse(githubAppInstallationId)
|
||||
}
|
||||
|
||||
if githubAppInstallation, ok := deploymentConfig.GithubAppInstallation(); ok {
|
||||
apiInstallation := ToInstallation(githubAppInstallation)
|
||||
res.GithubAppInstallation = apiInstallation
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func ToWorkflowVersion(workflow *db.WorkflowModel, version *db.WorkflowVersionModel) (*gen.WorkflowVersion, error) {
|
||||
res := &gen.WorkflowVersion{
|
||||
Metadata: *toAPIMetadata(version.ID, version.CreatedAt, version.UpdatedAt),
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/authz"
|
||||
apitokens "github.com/hatchet-dev/hatchet/api/v1/server/handlers/api-tokens"
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/handlers/events"
|
||||
githubapp "github.com/hatchet-dev/hatchet/api/v1/server/handlers/github-app"
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/handlers/metadata"
|
||||
stepruns "github.com/hatchet-dev/hatchet/api/v1/server/handlers/step-runs"
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/handlers/tenants"
|
||||
@@ -33,18 +34,20 @@ type apiService struct {
|
||||
*metadata.MetadataService
|
||||
*apitokens.APITokenService
|
||||
*stepruns.StepRunService
|
||||
*githubapp.GithubAppService
|
||||
}
|
||||
|
||||
func newAPIService(config *server.ServerConfig) *apiService {
|
||||
return &apiService{
|
||||
UserService: users.NewUserService(config),
|
||||
TenantService: tenants.NewTenantService(config),
|
||||
EventService: events.NewEventService(config),
|
||||
WorkflowService: workflows.NewWorkflowService(config),
|
||||
WorkerService: workers.NewWorkerService(config),
|
||||
MetadataService: metadata.NewMetadataService(config),
|
||||
APITokenService: apitokens.NewAPITokenService(config),
|
||||
StepRunService: stepruns.NewStepRunService(config),
|
||||
UserService: users.NewUserService(config),
|
||||
TenantService: tenants.NewTenantService(config),
|
||||
EventService: events.NewEventService(config),
|
||||
WorkflowService: workflows.NewWorkflowService(config),
|
||||
WorkerService: workers.NewWorkerService(config),
|
||||
MetadataService: metadata.NewMetadataService(config),
|
||||
APITokenService: apitokens.NewAPITokenService(config),
|
||||
StepRunService: stepruns.NewStepRunService(config),
|
||||
GithubAppService: githubapp.NewGithubAppService(config),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +163,16 @@ func (t *APIServer) Run(ctx context.Context) error {
|
||||
return worker, worker.TenantID, nil
|
||||
})
|
||||
|
||||
populatorMW.RegisterGetter("gh-installation", func(config *server.ServerConfig, parentId, id string) (result interface{}, uniqueParentId string, err error) {
|
||||
ghInstallation, err := config.Repository.Github().ReadGithubAppInstallationByID(id)
|
||||
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return ghInstallation, "", nil
|
||||
})
|
||||
|
||||
authnMW := authn.NewAuthN(t.config)
|
||||
authzMW := authz.NewAuthZ(t.config)
|
||||
|
||||
|
||||
+61
-4
@@ -3,11 +3,14 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/api/v1/server/run"
|
||||
"github.com/hatchet-dev/hatchet/internal/config/loader"
|
||||
"github.com/hatchet-dev/hatchet/internal/services/worker"
|
||||
"github.com/hatchet-dev/hatchet/pkg/cmdutils"
|
||||
)
|
||||
|
||||
@@ -63,14 +66,68 @@ func startServerOrDie(cf *loader.ConfigLoader, interruptCh <-chan interface{}) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
errCh := make(chan error)
|
||||
ctx, cancel := cmdutils.InterruptContextFromChan(interruptCh)
|
||||
defer cancel()
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
runner := run.NewAPIServer(sc)
|
||||
if sc.InternalClient != nil {
|
||||
wg.Add(1)
|
||||
|
||||
err = runner.Run(ctx)
|
||||
w, err := worker.NewWorker(
|
||||
worker.WithRepository(sc.Repository),
|
||||
worker.WithClient(sc.InternalClient),
|
||||
worker.WithVCSProviders(sc.VCSProviders),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
err := w.Start(ctx)
|
||||
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
runner := run.NewAPIServer(sc)
|
||||
|
||||
err = runner.Run(ctx)
|
||||
|
||||
if err != nil {
|
||||
errCh <- err
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
Loop:
|
||||
for {
|
||||
select {
|
||||
case err := <-errCh:
|
||||
fmt.Fprintf(os.Stderr, "%s", err)
|
||||
|
||||
// exit with non-zero exit code
|
||||
os.Exit(1) //nolint:gocritic
|
||||
case <-interruptCh:
|
||||
break Loop
|
||||
}
|
||||
}
|
||||
|
||||
cancel()
|
||||
|
||||
// TODO: should wait with a timeout
|
||||
// wg.Wait()
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
AcceptInviteRequest,
|
||||
CreateAPITokenRequest,
|
||||
CreateAPITokenResponse,
|
||||
CreatePullRequestFromStepRun,
|
||||
CreateTenantInviteRequest,
|
||||
CreateTenantRequest,
|
||||
EventData,
|
||||
@@ -25,7 +26,11 @@ import {
|
||||
EventOrderByDirection,
|
||||
EventOrderByField,
|
||||
EventSearch,
|
||||
LinkGithubRepositoryRequest,
|
||||
ListAPITokensResponse,
|
||||
ListGithubAppInstallationsResponse,
|
||||
ListGithubBranchesResponse,
|
||||
ListGithubReposResponse,
|
||||
RejectInviteRequest,
|
||||
ReplayEventRequest,
|
||||
RerunStepRunRequest,
|
||||
@@ -89,11 +94,11 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
|
||||
* @description Starts the OAuth flow
|
||||
*
|
||||
* @tags User
|
||||
* @name UserUpdateOauthStart
|
||||
* @name UserUpdateGoogleOauthStart
|
||||
* @summary Start OAuth flow
|
||||
* @request GET:/api/v1/users/google/start
|
||||
*/
|
||||
userUpdateOauthStart = (params: RequestParams = {}) =>
|
||||
userUpdateGoogleOauthStart = (params: RequestParams = {}) =>
|
||||
this.request<any, void>({
|
||||
path: `/api/v1/users/google/start`,
|
||||
method: "GET",
|
||||
@@ -103,16 +108,76 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
|
||||
* @description Completes the OAuth flow
|
||||
*
|
||||
* @tags User
|
||||
* @name UserUpdateOauthCallback
|
||||
* @name UserUpdateGoogleOauthCallback
|
||||
* @summary Complete OAuth flow
|
||||
* @request GET:/api/v1/users/google/callback
|
||||
*/
|
||||
userUpdateOauthCallback = (params: RequestParams = {}) =>
|
||||
userUpdateGoogleOauthCallback = (params: RequestParams = {}) =>
|
||||
this.request<any, void>({
|
||||
path: `/api/v1/users/google/callback`,
|
||||
method: "GET",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Starts the OAuth flow
|
||||
*
|
||||
* @tags User
|
||||
* @name UserUpdateGithubOauthStart
|
||||
* @summary Start OAuth flow
|
||||
* @request GET:/api/v1/users/github/start
|
||||
* @secure
|
||||
*/
|
||||
userUpdateGithubOauthStart = (params: RequestParams = {}) =>
|
||||
this.request<any, void>({
|
||||
path: `/api/v1/users/github/start`,
|
||||
method: "GET",
|
||||
secure: true,
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Completes the OAuth flow
|
||||
*
|
||||
* @tags User
|
||||
* @name UserUpdateGithubOauthCallback
|
||||
* @summary Complete OAuth flow
|
||||
* @request GET:/api/v1/users/github/callback
|
||||
* @secure
|
||||
*/
|
||||
userUpdateGithubOauthCallback = (params: RequestParams = {}) =>
|
||||
this.request<any, void>({
|
||||
path: `/api/v1/users/github/callback`,
|
||||
method: "GET",
|
||||
secure: true,
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Github App global webhook
|
||||
*
|
||||
* @tags Github
|
||||
* @name GithubUpdateGlobalWebhook
|
||||
* @summary Github app global webhook
|
||||
* @request POST:/api/v1/github/webhook
|
||||
*/
|
||||
githubUpdateGlobalWebhook = (params: RequestParams = {}) =>
|
||||
this.request<void, APIErrors>({
|
||||
path: `/api/v1/github/webhook`,
|
||||
method: "POST",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Github App tenant webhook
|
||||
*
|
||||
* @tags Github
|
||||
* @name GithubUpdateTenantWebhook
|
||||
* @summary Github app tenant webhook
|
||||
* @request POST:/api/v1/github/webhook/{webhook}
|
||||
*/
|
||||
githubUpdateTenantWebhook = (webhook: string, params: RequestParams = {}) =>
|
||||
this.request<void, APIErrors>({
|
||||
path: `/api/v1/github/webhook/${webhook}`,
|
||||
method: "POST",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Gets the current user
|
||||
*
|
||||
@@ -620,6 +685,44 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Link a github repository to a workflow
|
||||
*
|
||||
* @tags Workflow
|
||||
* @name WorkflowUpdateLinkGithub
|
||||
* @summary Link github repository
|
||||
* @request POST:/api/v1/workflows/{workflow}/link-github
|
||||
* @secure
|
||||
*/
|
||||
workflowUpdateLinkGithub = (workflow: string, data: LinkGithubRepositoryRequest, params: RequestParams = {}) =>
|
||||
this.request<Workflow, APIErrors>({
|
||||
path: `/api/v1/workflows/${workflow}/link-github`,
|
||||
method: "POST",
|
||||
body: data,
|
||||
secure: true,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Create a pull request for a workflow
|
||||
*
|
||||
* @tags Workflow
|
||||
* @name StepRunUpdateCreatePr
|
||||
* @summary Create pull request
|
||||
* @request POST:/api/v1/step-runs/{step-run}/create-pr
|
||||
* @secure
|
||||
*/
|
||||
stepRunUpdateCreatePr = (stepRun: string, data: CreatePullRequestFromStepRun, params: RequestParams = {}) =>
|
||||
this.request<CreatePullRequestFromStepRun, APIErrors>({
|
||||
path: `/api/v1/step-runs/${stepRun}/create-pr`,
|
||||
method: "POST",
|
||||
body: data,
|
||||
secure: true,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Get all workflow runs for a tenant
|
||||
*
|
||||
@@ -754,4 +857,60 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description List Github App installations
|
||||
*
|
||||
* @tags Github
|
||||
* @name GithubAppListInstallations
|
||||
* @summary List Github App installations
|
||||
* @request GET:/api/v1/github-app/installations
|
||||
* @secure
|
||||
*/
|
||||
githubAppListInstallations = (params: RequestParams = {}) =>
|
||||
this.request<ListGithubAppInstallationsResponse, APIErrors>({
|
||||
path: `/api/v1/github-app/installations`,
|
||||
method: "GET",
|
||||
secure: true,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description List Github App repositories
|
||||
*
|
||||
* @tags Github
|
||||
* @name GithubAppListRepos
|
||||
* @summary List Github App repositories
|
||||
* @request GET:/api/v1/github-app/installations/{gh-installation}/repos
|
||||
* @secure
|
||||
*/
|
||||
githubAppListRepos = (ghInstallation: string, params: RequestParams = {}) =>
|
||||
this.request<ListGithubReposResponse, APIErrors>({
|
||||
path: `/api/v1/github-app/installations/${ghInstallation}/repos`,
|
||||
method: "GET",
|
||||
secure: true,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description List Github App branches
|
||||
*
|
||||
* @tags Github
|
||||
* @name GithubAppListBranches
|
||||
* @summary List Github App branches
|
||||
* @request GET:/api/v1/github-app/installations/{gh-installation}/repos/{gh-repo-owner}/{gh-repo-name}/branches
|
||||
* @secure
|
||||
*/
|
||||
githubAppListBranches = (
|
||||
ghInstallation: string,
|
||||
ghRepoOwner: string,
|
||||
ghRepoName: string,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
this.request<ListGithubBranchesResponse, APIErrors>({
|
||||
path: `/api/v1/github-app/installations/${ghInstallation}/repos/${ghRepoOwner}/${ghRepoName}/branches`,
|
||||
method: "GET",
|
||||
secure: true,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -320,6 +320,24 @@ export interface Workflow {
|
||||
lastRun?: WorkflowRun;
|
||||
/** The jobs of the workflow. */
|
||||
jobs?: Job[];
|
||||
deployment?: WorkflowDeploymentConfig;
|
||||
}
|
||||
|
||||
export interface WorkflowDeploymentConfig {
|
||||
metadata: APIResourceMeta;
|
||||
/** The repository name. */
|
||||
gitRepoName: string;
|
||||
/** The repository owner. */
|
||||
gitRepoOwner: string;
|
||||
/** The repository branch. */
|
||||
gitRepoBranch: string;
|
||||
/** The Github App installation. */
|
||||
githubAppInstallation?: GithubAppInstallation;
|
||||
/**
|
||||
* The id of the Github App installation.
|
||||
* @format uuid
|
||||
*/
|
||||
githubAppInstallationId: string;
|
||||
}
|
||||
|
||||
export interface WorkflowVersionMeta {
|
||||
@@ -579,3 +597,48 @@ export interface RerunStepRunRequest {
|
||||
export interface TriggerWorkflowRunRequest {
|
||||
input: object;
|
||||
}
|
||||
|
||||
export interface LinkGithubRepositoryRequest {
|
||||
/**
|
||||
* The repository name.
|
||||
* @minLength 36
|
||||
* @maxLength 36
|
||||
*/
|
||||
installationId: string;
|
||||
/** The repository name. */
|
||||
gitRepoName: string;
|
||||
/** The repository owner. */
|
||||
gitRepoOwner: string;
|
||||
/** The repository branch. */
|
||||
gitRepoBranch: string;
|
||||
}
|
||||
|
||||
export interface GithubBranch {
|
||||
branch_name: string;
|
||||
is_default: boolean;
|
||||
}
|
||||
|
||||
export interface GithubRepo {
|
||||
repo_owner: string;
|
||||
repo_name: string;
|
||||
}
|
||||
|
||||
export interface GithubAppInstallation {
|
||||
metadata: APIResourceMeta;
|
||||
installation_settings_url: string;
|
||||
account_name: string;
|
||||
account_avatar_url: string;
|
||||
}
|
||||
|
||||
export interface ListGithubAppInstallationsResponse {
|
||||
pagination: PaginationResponse;
|
||||
rows: GithubAppInstallation[];
|
||||
}
|
||||
|
||||
export type ListGithubReposResponse = GithubRepo[];
|
||||
|
||||
export type ListGithubBranchesResponse = GithubBranch[];
|
||||
|
||||
export interface CreatePullRequestFromStepRun {
|
||||
branchName: string;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createQueryKeyStore } from '@lukemorales/query-key-factory';
|
||||
|
||||
import api from './api';
|
||||
import invariant from 'tiny-invariant';
|
||||
|
||||
type ListEventQuery = Parameters<typeof api.eventList>[1];
|
||||
type ListWorkflowRunsQuery = Parameters<typeof api.workflowRunList>[1];
|
||||
@@ -102,4 +103,36 @@ export const queries = createQueryKeyStore({
|
||||
queryFn: async () => (await api.workerGet(worker)).data,
|
||||
}),
|
||||
},
|
||||
github: {
|
||||
listInstallations: {
|
||||
queryKey: ['github-app:list:installations'],
|
||||
queryFn: async () => (await api.githubAppListInstallations()).data,
|
||||
},
|
||||
listRepos: (installation?: string) => ({
|
||||
queryKey: ['github-app:list:repos', installation],
|
||||
queryFn: async () => {
|
||||
invariant(installation, 'Installation must be set');
|
||||
const res = (await api.githubAppListRepos(installation)).data;
|
||||
return res;
|
||||
},
|
||||
enabled: !!installation,
|
||||
}),
|
||||
listBranches: (
|
||||
installation?: string,
|
||||
repoOwner?: string,
|
||||
repoName?: string,
|
||||
) => ({
|
||||
queryKey: ['github-app:list:branches', installation, repoOwner, repoName],
|
||||
queryFn: async () => {
|
||||
invariant(installation, 'Installation must be set');
|
||||
invariant(repoOwner, 'Repo owner must be set');
|
||||
invariant(repoName, 'Repo name must be set');
|
||||
const res = (
|
||||
await api.githubAppListBranches(installation, repoOwner, repoName)
|
||||
).data;
|
||||
return res;
|
||||
},
|
||||
enabled: !!installation && !!repoOwner && !!repoName,
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import { DataTableColumnHeader } from '../../../../components/molecules/data-table/data-table-column-header';
|
||||
import { GithubAppInstallation } from '@/lib/api';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { GearIcon } from '@radix-ui/react-icons';
|
||||
|
||||
export const columns = (): ColumnDef<GithubAppInstallation>[] => {
|
||||
return [
|
||||
{
|
||||
accessorKey: 'repository',
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="Account name" />
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<div className="flex flex-row gap-4 items-center">
|
||||
<Avatar className="w-6 h-6">
|
||||
<AvatarImage src={row.original.account_avatar_url} />
|
||||
<AvatarFallback />
|
||||
</Avatar>
|
||||
<div>{row.original.account_name}</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
{
|
||||
accessorKey: 'settings',
|
||||
header: ({ column }) => (
|
||||
<DataTableColumnHeader column={column} title="Settings" />
|
||||
),
|
||||
cell: ({ row }) => {
|
||||
return (
|
||||
<a
|
||||
href={row.original.installation_settings_url}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<Button variant="ghost" className="flex flex-row gap-2 px-2">
|
||||
<GearIcon className="h-4 w-4" />
|
||||
Configure
|
||||
</Button>
|
||||
</a>
|
||||
);
|
||||
},
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
];
|
||||
};
|
||||
@@ -51,8 +51,7 @@ export function UpdateInviteForm({
|
||||
},
|
||||
});
|
||||
|
||||
const roleError =
|
||||
errors.role?.message?.toString() || props.fieldErrors?.password;
|
||||
const roleError = errors.role?.message?.toString() || props.fieldErrors?.role;
|
||||
|
||||
return (
|
||||
<DialogContent className="w-fit max-w-[80%] min-w-[500px]">
|
||||
|
||||
@@ -19,6 +19,7 @@ import { DataTable } from '@/components/molecules/data-table/data-table';
|
||||
import { columns } from './components/invites-columns';
|
||||
import { columns as membersColumns } from './components/members-columns';
|
||||
import { columns as apiTokensColumns } from './components/api-tokens-columns';
|
||||
import { columns as githubInstallationsColumns } from './components/github-installations-columns';
|
||||
import { UpdateInviteForm } from './components/update-invite-form';
|
||||
import { DeleteInviteForm } from './components/delete-invite-form';
|
||||
import { CreateTokenDialog } from './components/create-token-dialog';
|
||||
@@ -39,6 +40,8 @@ export default function TenantSettings() {
|
||||
<InvitesList />
|
||||
<Separator className="my-4" />
|
||||
<TokensList />
|
||||
<Separator className="my-4" />
|
||||
<GithubInstallationsList />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -388,3 +391,32 @@ function RevokeToken({
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
function GithubInstallationsList() {
|
||||
const listInstallationsQuery = useQuery({
|
||||
...queries.github.listInstallations,
|
||||
});
|
||||
|
||||
const cols = githubInstallationsColumns();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-row justify-between items-center">
|
||||
<h3 className="text-xl font-semibold leading-tight text-foreground">
|
||||
Github Accounts
|
||||
</h3>
|
||||
<a href="/api/v1/users/github/start">
|
||||
<Button key="create-api-token">Link new account</Button>
|
||||
</a>
|
||||
</div>
|
||||
<Separator className="my-4" />
|
||||
<DataTable
|
||||
isLoading={listInstallationsQuery.isLoading}
|
||||
columns={cols}
|
||||
data={listInstallationsQuery.data?.rows || []}
|
||||
filters={[]}
|
||||
getRowId={(row) => row.metadata.id}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+292
@@ -0,0 +1,292 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import api, { LinkGithubRepositoryRequest, Workflow, queries } from '@/lib/api';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useApiError } from '@/lib/hooks';
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { PlusIcon } from '@heroicons/react/24/outline';
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { z } from 'zod';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Label } from '@/components/ui/label';
|
||||
|
||||
const schema = z.object({
|
||||
installation: z.string(),
|
||||
repoOwnerName: z.string(),
|
||||
branch: z.string(),
|
||||
});
|
||||
|
||||
export function DeploymentSettingsForm({
|
||||
workflow,
|
||||
show,
|
||||
onClose,
|
||||
}: {
|
||||
workflow: Workflow;
|
||||
show: boolean;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const [errors, setErrors] = useState<string[]>([]);
|
||||
|
||||
const { handleApiError } = useApiError({
|
||||
setErrors,
|
||||
});
|
||||
|
||||
const linkGithubMutation = useMutation({
|
||||
mutationKey: ['workflow:update:link-github', workflow?.metadata.id],
|
||||
mutationFn: async (input: LinkGithubRepositoryRequest) => {
|
||||
if (!workflow) {
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await api.workflowUpdateLinkGithub(
|
||||
workflow?.metadata.id,
|
||||
input,
|
||||
);
|
||||
|
||||
return res.data;
|
||||
},
|
||||
onMutate: () => {
|
||||
setErrors([]);
|
||||
},
|
||||
onSuccess: () => {
|
||||
onClose();
|
||||
},
|
||||
onError: handleApiError,
|
||||
});
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={!!workflow && show}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent className="sm:max-w-[625px] py-12">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Deployment settings</DialogTitle>
|
||||
<DialogDescription>
|
||||
You can change the deployment settings of your workflow here.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<InnerForm
|
||||
workflow={workflow}
|
||||
onSubmit={(opts) => {
|
||||
const repoOwner = getRepoOwner(opts.repoOwnerName);
|
||||
const repoName = getRepoName(opts.repoOwnerName);
|
||||
|
||||
if (!repoOwner || !repoName) {
|
||||
return;
|
||||
}
|
||||
|
||||
linkGithubMutation.mutate({
|
||||
installationId: opts.installation,
|
||||
gitRepoOwner: repoOwner,
|
||||
gitRepoName: repoName,
|
||||
gitRepoBranch: opts.branch,
|
||||
});
|
||||
}}
|
||||
isLoading={linkGithubMutation.isPending}
|
||||
errors={errors}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
interface InnerFormProps {
|
||||
onSubmit: (opts: z.infer<typeof schema>) => void;
|
||||
isLoading: boolean;
|
||||
errors?: string[];
|
||||
workflow: Workflow;
|
||||
}
|
||||
|
||||
function InnerForm({ workflow, onSubmit, isLoading, errors }: InnerFormProps) {
|
||||
const { watch, handleSubmit, control, setValue } = useForm<
|
||||
z.infer<typeof schema>
|
||||
>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
installation: workflow.deployment?.githubAppInstallationId,
|
||||
repoOwnerName:
|
||||
workflow.deployment?.gitRepoOwner &&
|
||||
workflow.deployment?.gitRepoName &&
|
||||
getRepoOwnerName(
|
||||
workflow.deployment?.gitRepoOwner,
|
||||
workflow.deployment?.gitRepoName,
|
||||
),
|
||||
branch: workflow.deployment?.gitRepoBranch,
|
||||
},
|
||||
});
|
||||
|
||||
const installation = watch('installation');
|
||||
const repoOwnerName = watch('repoOwnerName');
|
||||
const branch = watch('branch');
|
||||
|
||||
const listInstallationsQuery = useQuery({
|
||||
...queries.github.listInstallations,
|
||||
});
|
||||
|
||||
const listReposQuery = useQuery({
|
||||
...queries.github.listRepos(installation),
|
||||
});
|
||||
|
||||
const listBranchesQuery = useQuery({
|
||||
...queries.github.listBranches(
|
||||
installation,
|
||||
getRepoOwner(repoOwnerName),
|
||||
getRepoName(repoOwnerName),
|
||||
),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
listInstallationsQuery.isSuccess &&
|
||||
listInstallationsQuery.data.rows.length > 0 &&
|
||||
!installation
|
||||
) {
|
||||
setValue(
|
||||
'installation',
|
||||
listInstallationsQuery.data?.rows[0]?.metadata.id,
|
||||
);
|
||||
}
|
||||
}, [listInstallationsQuery, setValue, installation]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid gap-4">
|
||||
<Label htmlFor="role">Github account</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="installation"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<Select
|
||||
onValueChange={(value: string) => {
|
||||
field.onChange(value);
|
||||
setValue('repoOwnerName', '');
|
||||
setValue('branch', '');
|
||||
}}
|
||||
{...field}
|
||||
>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue id="role" placeholder="Choose account" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{listInstallationsQuery.data?.rows.map((i) => (
|
||||
<SelectItem key={i.metadata.id} value={i.metadata.id}>
|
||||
{i.account_name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="role">Github repo</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="repoOwnerName"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<Select
|
||||
onValueChange={(value) => {
|
||||
field.onChange(value);
|
||||
setValue('branch', '');
|
||||
}}
|
||||
{...field}
|
||||
>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue id="role" placeholder="Choose repository" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{listReposQuery.data?.map((i) => (
|
||||
<SelectItem
|
||||
key={i.repo_owner + i.repo_name}
|
||||
value={getRepoOwnerName(i.repo_owner, i.repo_name)}
|
||||
>
|
||||
{i.repo_owner}/{i.repo_name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<Label htmlFor="role">Github branch</Label>
|
||||
<Controller
|
||||
control={control}
|
||||
name="branch"
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<Select onValueChange={field.onChange} {...field}>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue id="role" placeholder="Choose branch" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{listBranchesQuery.data?.map((i) => (
|
||||
<SelectItem key={i.branch_name} value={i.branch_name}>
|
||||
{i.branch_name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{errors?.map((error, index) => (
|
||||
<div key={index} className="text-red-500 text-sm">
|
||||
{error}
|
||||
</div>
|
||||
))}
|
||||
<Button
|
||||
disabled={!installation || !repoOwnerName || !branch}
|
||||
onClick={handleSubmit(onSubmit)}
|
||||
>
|
||||
{isLoading && <PlusIcon className="h-4 w-4 animate-spin" />}
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function getRepoOwnerName(repoOwner: string, repoName: string) {
|
||||
return `${repoOwner}::${repoName}`;
|
||||
}
|
||||
|
||||
function getRepoOwner(repoOwnerName?: string) {
|
||||
if (!repoOwnerName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const splArr = repoOwnerName.split('::');
|
||||
if (splArr.length > 1) {
|
||||
return splArr[0];
|
||||
}
|
||||
}
|
||||
|
||||
function getRepoName(repoOwnerName?: string) {
|
||||
if (!repoOwnerName) {
|
||||
return;
|
||||
}
|
||||
|
||||
const splArr = repoOwnerName.split('::');
|
||||
if (splArr.length > 1) {
|
||||
return splArr[1];
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
useLoaderData,
|
||||
useOutletContext,
|
||||
useParams,
|
||||
useRevalidator,
|
||||
} from 'react-router-dom';
|
||||
import invariant from 'tiny-invariant';
|
||||
import { columns } from '../../workflow-runs/components/workflow-runs-columns';
|
||||
@@ -22,6 +23,7 @@ import WorkflowVisualizer from './components/workflow-visualizer';
|
||||
import { TriggerWorkflowForm } from './components/trigger-workflow-form';
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { DeploymentSettingsForm } from './components/deployment-settings-form';
|
||||
|
||||
type WorkflowWithVersion = {
|
||||
workflow: Workflow;
|
||||
@@ -69,6 +71,7 @@ export async function loader({
|
||||
export default function ExpandedWorkflow() {
|
||||
const [triggerWorkflow, setTriggerWorkflow] = useState(false);
|
||||
const loaderData = useLoaderData() as Awaited<ReturnType<typeof loader>>;
|
||||
const revalidator = useRevalidator();
|
||||
|
||||
if (!loaderData) {
|
||||
return <Loading />;
|
||||
@@ -127,6 +130,14 @@ export default function ExpandedWorkflow() {
|
||||
</h3>
|
||||
<Separator className="my-4" />
|
||||
<RecentRunsList />
|
||||
<h3 className="text-xl font-bold leading-tight text-foreground mt-8">
|
||||
Deployment Settings
|
||||
</h3>
|
||||
<Separator className="my-4" />
|
||||
<DeploymentSettings
|
||||
workflow={workflow}
|
||||
refetch={revalidator.revalidate}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -160,3 +171,33 @@ function RecentRunsList() {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function DeploymentSettings({
|
||||
workflow,
|
||||
refetch,
|
||||
}: {
|
||||
workflow: Workflow;
|
||||
refetch?: () => void;
|
||||
}) {
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
className="text-sm"
|
||||
onClick={() => setShow(true)}
|
||||
variant="outline"
|
||||
>
|
||||
Change settings
|
||||
</Button>
|
||||
<DeploymentSettingsForm
|
||||
workflow={workflow}
|
||||
show={show}
|
||||
onClose={() => {
|
||||
setShow(false);
|
||||
refetch?.();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ require (
|
||||
github.com/fatih/color v1.16.0
|
||||
github.com/getkin/kin-openapi v0.122.0
|
||||
github.com/go-co-op/gocron/v2 v2.1.2
|
||||
github.com/google/go-github/v57 v57.0.0
|
||||
github.com/gorilla/securecookie v1.1.2
|
||||
github.com/gorilla/sessions v1.2.2
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
@@ -48,7 +49,9 @@ require (
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
@@ -82,6 +85,7 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.9.0
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.25.0
|
||||
|
||||
@@ -50,6 +50,8 @@ github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.9.0 h1:HmxIYqnxubRYcYGRc5v3wUekmo5Wv2uX3gukmWJ0AFk=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.9.0/go.mod h1:wmkTDJf8CmVypxE8ijIStFnKoTa6solK5QfdmJrP9KI=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
@@ -124,6 +126,8 @@ github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncV
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -172,6 +176,10 @@ github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-github/v57 v57.0.0 h1:L+Y3UPTY8ALM8x+TV0lg+IEBI+upibemtBD8Q9u7zHs=
|
||||
github.com/google/go-github/v57 v57.0.0/go.mod h1:s0omdnye0hvK/ecLvpsGfJMiRt85PimQh4oygmLIxHw=
|
||||
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
|
||||
@@ -18,12 +18,17 @@ import (
|
||||
"github.com/hatchet-dev/hatchet/internal/config/loader/loaderutils"
|
||||
"github.com/hatchet-dev/hatchet/internal/config/server"
|
||||
"github.com/hatchet-dev/hatchet/internal/encryption"
|
||||
"github.com/hatchet-dev/hatchet/internal/integrations/vcs"
|
||||
"github.com/hatchet-dev/hatchet/internal/integrations/vcs/github"
|
||||
"github.com/hatchet-dev/hatchet/internal/logger"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
"github.com/hatchet-dev/hatchet/internal/services/ingestor"
|
||||
"github.com/hatchet-dev/hatchet/internal/taskqueue/rabbitmq"
|
||||
"github.com/hatchet-dev/hatchet/internal/validator"
|
||||
"github.com/hatchet-dev/hatchet/pkg/client"
|
||||
|
||||
clientconfig "github.com/hatchet-dev/hatchet/internal/config/client"
|
||||
)
|
||||
|
||||
// LoadDatabaseConfigFile loads the database config file via viper
|
||||
@@ -217,19 +222,77 @@ func GetServerConfigFromConfigfile(dc *database.Config, cf *server.ServerConfigF
|
||||
return nil, fmt.Errorf("could not create JWT manager: %w", err)
|
||||
}
|
||||
|
||||
vcsProviders := make(map[vcs.VCSRepositoryKind]vcs.VCSProvider)
|
||||
|
||||
if cf.VCS.Github.Enabled {
|
||||
var err error
|
||||
|
||||
githubAppConf, err := github.NewGithubAppConf(&oauth.Config{
|
||||
ClientID: cf.VCS.Github.GithubAppClientID,
|
||||
ClientSecret: cf.VCS.Github.GithubAppClientSecret,
|
||||
Scopes: []string{"read:user"},
|
||||
BaseURL: cf.Runtime.ServerURL,
|
||||
}, cf.VCS.Github.GithubAppName, cf.VCS.Github.GithubAppSecretPath, cf.VCS.Github.GithubAppWebhookSecret, cf.VCS.Github.GithubAppID)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
githubProvider := github.NewGithubVCSProvider(githubAppConf, dc.Repository, cf.Runtime.ServerURL, encryptionSvc)
|
||||
|
||||
vcsProviders[vcs.VCSRepositoryKindGithub] = githubProvider
|
||||
}
|
||||
|
||||
var internalClient client.Client
|
||||
|
||||
if cf.Runtime.WorkerEnabled {
|
||||
// get the internal tenant or create if it doesn't exist
|
||||
internalTenant, err := dc.Repository.Tenant().GetTenantBySlug("internal")
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get internal tenant: %w", err)
|
||||
}
|
||||
|
||||
tokenSuffix, err := encryption.GenerateRandomBytes(4)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate token suffix: %w", err)
|
||||
}
|
||||
|
||||
// generate a token for the internal client
|
||||
token, err := auth.JWTManager.GenerateTenantToken(internalTenant.ID, fmt.Sprintf("internal-%s", tokenSuffix))
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not generate internal token: %w", err)
|
||||
}
|
||||
|
||||
internalClient, err = client.NewFromConfigFile(
|
||||
&clientconfig.ClientConfigFile{
|
||||
Token: token,
|
||||
HostPort: cf.Runtime.GRPCBroadcastAddress,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create internal client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &server.ServerConfig{
|
||||
Runtime: cf.Runtime,
|
||||
Auth: auth,
|
||||
Encryption: encryptionSvc,
|
||||
Config: dc,
|
||||
TaskQueue: tq,
|
||||
Services: cf.Services,
|
||||
Logger: &l,
|
||||
TLSConfig: tls,
|
||||
SessionStore: ss,
|
||||
Validator: validator.NewDefaultValidator(),
|
||||
Ingestor: ingestor,
|
||||
OpenTelemetry: cf.OpenTelemetry,
|
||||
Runtime: cf.Runtime,
|
||||
Auth: auth,
|
||||
Encryption: encryptionSvc,
|
||||
Config: dc,
|
||||
TaskQueue: tq,
|
||||
Services: cf.Services,
|
||||
Logger: &l,
|
||||
TLSConfig: tls,
|
||||
SessionStore: ss,
|
||||
Validator: validator.NewDefaultValidator(),
|
||||
Ingestor: ingestor,
|
||||
OpenTelemetry: cf.OpenTelemetry,
|
||||
VCSProviders: vcsProviders,
|
||||
InternalClient: internalClient,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -12,9 +12,11 @@ import (
|
||||
"github.com/hatchet-dev/hatchet/internal/config/database"
|
||||
"github.com/hatchet-dev/hatchet/internal/config/shared"
|
||||
"github.com/hatchet-dev/hatchet/internal/encryption"
|
||||
"github.com/hatchet-dev/hatchet/internal/integrations/vcs"
|
||||
"github.com/hatchet-dev/hatchet/internal/services/ingestor"
|
||||
"github.com/hatchet-dev/hatchet/internal/taskqueue"
|
||||
"github.com/hatchet-dev/hatchet/internal/validator"
|
||||
"github.com/hatchet-dev/hatchet/pkg/client"
|
||||
)
|
||||
|
||||
type ServerConfigFile struct {
|
||||
@@ -33,6 +35,8 @@ type ServerConfigFile struct {
|
||||
Logger shared.LoggerConfigFile `mapstructure:"logger" json:"logger,omitempty"`
|
||||
|
||||
OpenTelemetry shared.OpenTelemetryConfigFile `mapstructure:"otel" json:"otel,omitempty"`
|
||||
|
||||
VCS ConfigFileVCS `mapstructure:"vcs" json:"vcs,omitempty"`
|
||||
}
|
||||
|
||||
// General server runtime options
|
||||
@@ -54,6 +58,9 @@ type ConfigFileRuntime struct {
|
||||
|
||||
// GRPCInsecure controls whether the grpc server is insecure or uses certs
|
||||
GRPCInsecure bool `mapstructure:"grpcInsecure" json:"grpcInsecure,omitempty" default:"false"`
|
||||
|
||||
// Whether the internal worker is enabled for this instance
|
||||
WorkerEnabled bool `mapstructure:"workerEnabled" json:"workerEnabled,omitempty" default:"false"`
|
||||
}
|
||||
|
||||
// Encryption options
|
||||
@@ -116,6 +123,20 @@ type ConfigFileAuth struct {
|
||||
Google ConfigFileAuthGoogle `mapstructure:"google" json:"google,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigFileVCS struct {
|
||||
Github ConfigFileGithub `mapstructure:"github" json:"github,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigFileGithub struct {
|
||||
Enabled bool `mapstructure:"enabled" json:"enabled"`
|
||||
GithubAppClientID string `mapstructure:"appClientID" json:"appClientID,omitempty"`
|
||||
GithubAppClientSecret string `mapstructure:"appClientSecret" json:"appClientSecret,omitempty"`
|
||||
GithubAppName string `mapstructure:"appName" json:"appName,omitempty"`
|
||||
GithubAppWebhookSecret string `mapstructure:"appWebhookSecret" json:"appWebhookSecret,omitempty"`
|
||||
GithubAppID string `mapstructure:"appID" json:"appID,omitempty"`
|
||||
GithubAppSecretPath string `mapstructure:"appSecretPath" json:"appSecretPath,omitempty"`
|
||||
}
|
||||
|
||||
type ConfigFileAuthGoogle struct {
|
||||
Enabled bool `mapstructure:"enabled" json:"enabled,omitempty" default:"false"`
|
||||
|
||||
@@ -175,6 +196,10 @@ type ServerConfig struct {
|
||||
Ingestor ingestor.Ingestor
|
||||
|
||||
OpenTelemetry shared.OpenTelemetryConfigFile
|
||||
|
||||
VCSProviders map[vcs.VCSRepositoryKind]vcs.VCSProvider
|
||||
|
||||
InternalClient client.Client
|
||||
}
|
||||
|
||||
func (c *ServerConfig) HasService(name string) bool {
|
||||
@@ -195,6 +220,7 @@ func BindAllEnv(v *viper.Viper) {
|
||||
_ = v.BindEnv("runtime.grpcBindAddress", "SERVER_GRPC_BIND_ADDRESS")
|
||||
_ = v.BindEnv("runtime.grpcBroadcastAddress", "SERVER_GRPC_BROADCAST_ADDRESS")
|
||||
_ = v.BindEnv("runtime.grpcInsecure", "SERVER_GRPC_INSECURE")
|
||||
_ = v.BindEnv("runtime.workerEnabled", "SERVER_WORKER_ENABLED")
|
||||
_ = v.BindEnv("services", "SERVER_SERVICES")
|
||||
|
||||
// encryption options
|
||||
@@ -242,4 +268,14 @@ func BindAllEnv(v *viper.Viper) {
|
||||
// otel options
|
||||
_ = v.BindEnv("otel.serviceName", "SERVER_OTEL_SERVICE_NAME")
|
||||
_ = v.BindEnv("otel.collectorURL", "SERVER_OTEL_COLLECTOR_URL")
|
||||
|
||||
// vcs options
|
||||
v.BindEnv("vcs.kind", "SERVER_VCS_KIND")
|
||||
v.BindEnv("vcs.github.enabled", "SERVER_VCS_GITHUB_ENABLED")
|
||||
v.BindEnv("vcs.github.appClientID", "SERVER_VCS_GITHUB_APP_CLIENT_ID")
|
||||
v.BindEnv("vcs.github.appClientSecret", "SERVER_VCS_GITHUB_APP_CLIENT_SECRET")
|
||||
v.BindEnv("vcs.github.appName", "SERVER_VCS_GITHUB_APP_NAME")
|
||||
v.BindEnv("vcs.github.appWebhookSecret", "SERVER_VCS_GITHUB_APP_WEBHOOK_SECRET")
|
||||
v.BindEnv("vcs.github.appID", "SERVER_VCS_GITHUB_APP_ID")
|
||||
v.BindEnv("vcs.github.appSecretPath", "SERVER_VCS_GITHUB_APP_SECRET_PATH")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
githubsdk "github.com/google/go-github/v57/github"
|
||||
)
|
||||
|
||||
type GithubBranch struct {
|
||||
*githubsdk.Branch
|
||||
}
|
||||
|
||||
func (g *GithubBranch) GetName() string {
|
||||
return g.Branch.GetName()
|
||||
}
|
||||
|
||||
func (g *GithubBranch) GetLatestRef() string {
|
||||
return g.GetCommit().GetSHA()
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
githubsdk "github.com/google/go-github/v57/github"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/internal/integrations/vcs"
|
||||
)
|
||||
|
||||
type GithubCommitsComparison struct {
|
||||
*githubsdk.CommitsComparison
|
||||
}
|
||||
|
||||
func (g *GithubCommitsComparison) GetFiles() []vcs.CommitFile {
|
||||
var res []vcs.CommitFile
|
||||
|
||||
for _, f := range g.CommitsComparison.Files {
|
||||
res = append(res, vcs.CommitFile{
|
||||
Name: f.GetFilename(),
|
||||
})
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
githubsdk "github.com/google/go-github/v57/github"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/internal/encryption"
|
||||
"github.com/hatchet-dev/hatchet/internal/integrations/vcs"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
)
|
||||
|
||||
type GithubVCSProvider struct {
|
||||
repo repository.Repository
|
||||
appConf *GithubAppConf
|
||||
serverURL string
|
||||
enc encryption.EncryptionService
|
||||
}
|
||||
|
||||
func NewGithubVCSProvider(appConf *GithubAppConf, repo repository.Repository, serverURL string, enc encryption.EncryptionService) GithubVCSProvider {
|
||||
return GithubVCSProvider{
|
||||
appConf: appConf,
|
||||
repo: repo,
|
||||
serverURL: serverURL,
|
||||
enc: enc,
|
||||
}
|
||||
}
|
||||
|
||||
func ToGithubVCSProvider(provider vcs.VCSProvider) (res GithubVCSProvider, err error) {
|
||||
res, ok := provider.(GithubVCSProvider)
|
||||
|
||||
if !ok {
|
||||
return res, fmt.Errorf("could not convert VCS provider to Github VCS provider: %w", err)
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (g GithubVCSProvider) GetGithubAppConfig() *GithubAppConf {
|
||||
return g.appConf
|
||||
}
|
||||
|
||||
func (g GithubVCSProvider) GetVCSRepositoryFromWorkflow(workflow *db.WorkflowModel) (vcs.VCSRepository, error) {
|
||||
var installationId string
|
||||
var deploymentConf *db.WorkflowDeploymentConfigModel
|
||||
var ok bool
|
||||
|
||||
if deploymentConf, ok = workflow.DeploymentConfig(); ok {
|
||||
if installationId, ok = deploymentConf.GithubAppInstallationID(); !ok {
|
||||
return nil, fmt.Errorf("module does not have github app installation id param set")
|
||||
}
|
||||
}
|
||||
|
||||
gai, err := g.repo.Github().ReadGithubAppInstallationByID(installationId)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client, err := g.appConf.GetGithubClient(int64(gai.InstallationID))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &GithubVCSRepository{
|
||||
repoOwner: deploymentConf.GitRepoOwner,
|
||||
repoName: deploymentConf.GitRepoName,
|
||||
serverURL: g.serverURL,
|
||||
client: client,
|
||||
repo: g.repo,
|
||||
enc: g.enc,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// func (g GithubVCSProvider) GetVCSRepositoryFromGAI(gai *models.GithubAppInstallation) (vcs.VCSRepository, error) {
|
||||
// }
|
||||
|
||||
type GithubVCSRepository struct {
|
||||
repoOwner, repoName string
|
||||
client *githubsdk.Client
|
||||
repo repository.Repository
|
||||
serverURL string
|
||||
enc encryption.EncryptionService
|
||||
}
|
||||
|
||||
// GetKind returns the kind of VCS provider -- used for downstream integrations
|
||||
func (g *GithubVCSRepository) GetKind() vcs.VCSRepositoryKind {
|
||||
return vcs.VCSRepositoryKindGithub
|
||||
}
|
||||
|
||||
func (g *GithubVCSRepository) GetRepoOwner() string {
|
||||
return g.repoOwner
|
||||
}
|
||||
|
||||
func (g *GithubVCSRepository) GetRepoName() string {
|
||||
return g.repoName
|
||||
}
|
||||
|
||||
// SetupRepository sets up a VCS repository on Hatchet.
|
||||
func (g *GithubVCSRepository) SetupRepository(tenantId string) error {
|
||||
repoOwner := g.GetRepoOwner()
|
||||
repoName := g.GetRepoName()
|
||||
|
||||
_, err := g.repo.Github().ReadGithubWebhook(tenantId, repoOwner, repoName)
|
||||
|
||||
if err != nil && !errors.Is(err, db.ErrNotFound) {
|
||||
return err
|
||||
} else if err != nil {
|
||||
opts, signingSecret, err := repository.NewGithubWebhookCreateOpts(g.enc, repoOwner, repoName)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gw, err := g.repo.Github().CreateGithubWebhook(tenantId, opts)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
webhookURL := fmt.Sprintf("%s/api/v1/teams/%s/github_incoming/%s", g.serverURL, tenantId, gw.ID)
|
||||
|
||||
_, _, err = g.client.Repositories.CreateHook(
|
||||
context.Background(), repoOwner, repoName, &githubsdk.Hook{
|
||||
Config: map[string]interface{}{
|
||||
"url": webhookURL,
|
||||
"content_type": "json",
|
||||
"secret": signingSecret,
|
||||
},
|
||||
Events: []string{"pull_request", "push"},
|
||||
Active: githubsdk.Bool(true),
|
||||
},
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetArchiveLink returns an archive link for a specific repo SHA
|
||||
func (g *GithubVCSRepository) GetArchiveLink(ref string) (*url.URL, error) {
|
||||
gURL, _, err := g.client.Repositories.GetArchiveLink(
|
||||
context.TODO(),
|
||||
g.GetRepoOwner(),
|
||||
g.GetRepoName(),
|
||||
githubsdk.Zipball,
|
||||
&githubsdk.RepositoryContentGetOptions{
|
||||
Ref: ref,
|
||||
},
|
||||
2,
|
||||
)
|
||||
|
||||
return gURL, err
|
||||
}
|
||||
|
||||
// GetBranch gets a full branch (name and sha)
|
||||
func (g *GithubVCSRepository) GetBranch(name string) (vcs.VCSBranch, error) {
|
||||
branchResp, _, err := g.client.Repositories.GetBranch(
|
||||
context.TODO(),
|
||||
g.GetRepoOwner(),
|
||||
g.GetRepoName(),
|
||||
name,
|
||||
2,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &GithubBranch{branchResp}, nil
|
||||
}
|
||||
|
||||
// ReadFile returns a file by a SHA reference or path
|
||||
func (g *GithubVCSRepository) ReadFile(ref, path string) (io.ReadCloser, error) {
|
||||
file, _, err := g.client.Repositories.DownloadContents(
|
||||
context.Background(),
|
||||
g.GetRepoOwner(),
|
||||
g.GetRepoName(),
|
||||
path,
|
||||
&githubsdk.RepositoryContentGetOptions{
|
||||
Ref: ref,
|
||||
},
|
||||
)
|
||||
|
||||
return file, err
|
||||
}
|
||||
|
||||
func (g *GithubVCSRepository) ReadDirectory(ref, path string) ([]vcs.DirectoryItem, error) {
|
||||
_, dirs, _, err := g.client.Repositories.GetContents(
|
||||
context.Background(),
|
||||
g.GetRepoOwner(),
|
||||
g.GetRepoName(),
|
||||
path,
|
||||
&githubsdk.RepositoryContentGetOptions{
|
||||
Ref: ref,
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := []vcs.DirectoryItem{}
|
||||
|
||||
for _, item := range dirs {
|
||||
if item.Type != nil && item.Name != nil {
|
||||
res = append(res, vcs.DirectoryItem{
|
||||
Type: *item.Type,
|
||||
Name: *item.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (g *GithubVCSRepository) CreateOrUpdatePullRequest(tenantId, workflowRunId string, opts *vcs.CreatePullRequestOpts) (*db.GithubPullRequestModel, error) {
|
||||
// determine if there's an open pull request for this workflow run
|
||||
prs, err := g.repo.WorkflowRun().ListPullRequestsForWorkflowRun(tenantId, workflowRunId, &repository.ListPullRequestsForWorkflowRunOpts{
|
||||
State: repository.StringPtr("open"),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(prs) > 0 {
|
||||
// double check that the PR is still open, cycle through PRs to find the first open one
|
||||
for _, pr := range prs {
|
||||
prCp := pr
|
||||
|
||||
ghPR, err := g.getPullRequest(tenantId, workflowRunId, &prCp)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if prCp.PullRequestState != ghPR.GetState() {
|
||||
defer g.repo.Github().UpdatePullRequest(tenantId, prCp.ID, &repository.UpdatePullRequestOpts{ // nolint: errcheck
|
||||
State: repository.StringPtr(ghPR.GetState()),
|
||||
})
|
||||
}
|
||||
|
||||
if ghPR.GetState() == "open" {
|
||||
return g.updatePullRequest(tenantId, workflowRunId, &prCp, opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we get here, we need to create a new PR
|
||||
return g.createPullRequest(tenantId, workflowRunId, opts)
|
||||
}
|
||||
|
||||
func (g *GithubVCSRepository) getPullRequest(tenantId, workflowRunId string, pr *db.GithubPullRequestModel) (*githubsdk.PullRequest, error) {
|
||||
ghPR, _, err := g.client.PullRequests.Get(
|
||||
context.Background(),
|
||||
pr.RepositoryOwner,
|
||||
pr.RepositoryName,
|
||||
pr.PullRequestNumber,
|
||||
)
|
||||
|
||||
return ghPR, err
|
||||
}
|
||||
|
||||
func (g *GithubVCSRepository) updatePullRequest(tenantId, workflowRunId string, pr *db.GithubPullRequestModel, opts *vcs.CreatePullRequestOpts) (*db.GithubPullRequestModel, error) {
|
||||
err := commitFiles(
|
||||
g.client,
|
||||
opts.Files,
|
||||
opts.GitRepoOwner,
|
||||
opts.GitRepoName,
|
||||
opts.HeadBranchName,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not commit files: %w", err)
|
||||
}
|
||||
|
||||
return pr, nil
|
||||
}
|
||||
|
||||
func (g *GithubVCSRepository) createPullRequest(tenantId, workflowRunId string, opts *vcs.CreatePullRequestOpts) (*db.GithubPullRequestModel, error) {
|
||||
var baseBranch string
|
||||
|
||||
if opts.BaseBranch == nil {
|
||||
repo, _, err := g.client.Repositories.Get(
|
||||
context.TODO(),
|
||||
opts.GitRepoOwner,
|
||||
opts.GitRepoName,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseBranch = repo.GetDefaultBranch()
|
||||
}
|
||||
|
||||
err := createNewBranch(g.client, opts.GitRepoOwner, opts.GitRepoName, baseBranch, opts.HeadBranchName)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not create PR: %w", err)
|
||||
}
|
||||
|
||||
err = commitFiles(
|
||||
g.client,
|
||||
opts.Files,
|
||||
opts.GitRepoOwner,
|
||||
opts.GitRepoName,
|
||||
opts.HeadBranchName,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not commit files: %w", err)
|
||||
}
|
||||
|
||||
pr, _, err := g.client.PullRequests.Create(
|
||||
context.Background(), opts.GitRepoOwner, opts.GitRepoName, &githubsdk.NewPullRequest{
|
||||
Title: githubsdk.String(opts.Title),
|
||||
Base: githubsdk.String(baseBranch),
|
||||
Head: githubsdk.String(opts.HeadBranchName),
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return g.repo.WorkflowRun().CreateWorkflowRunPullRequest(tenantId, workflowRunId, &repository.CreateWorkflowRunPullRequestOpts{
|
||||
RepositoryOwner: opts.GitRepoOwner,
|
||||
RepositoryName: opts.GitRepoName,
|
||||
PullRequestID: int(pr.GetID()),
|
||||
PullRequestTitle: opts.Title,
|
||||
PullRequestNumber: pr.GetNumber(),
|
||||
PullRequestHeadBranch: opts.HeadBranchName,
|
||||
PullRequestBaseBranch: baseBranch,
|
||||
PullRequestState: pr.GetState(),
|
||||
})
|
||||
}
|
||||
|
||||
// CompareCommits compares a base commit with a head commit
|
||||
func (g *GithubVCSRepository) CompareCommits(base, head string) (vcs.VCSCommitsComparison, error) {
|
||||
commitsRes, _, err := g.client.Repositories.CompareCommits(
|
||||
context.Background(),
|
||||
g.GetRepoOwner(),
|
||||
g.GetRepoName(),
|
||||
base,
|
||||
head,
|
||||
&githubsdk.ListOptions{},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &GithubCommitsComparison{commitsRes}, nil
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/bradleyfalzon/ghinstallation/v2"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/internal/auth/oauth"
|
||||
|
||||
githubsdk "github.com/google/go-github/v57/github"
|
||||
)
|
||||
|
||||
const (
|
||||
GithubAuthURL string = "https://github.com/login/oauth/authorize"
|
||||
GithubTokenURL string = "https://github.com/login/oauth/access_token" // #nosec G101
|
||||
)
|
||||
|
||||
type GithubAppConf struct {
|
||||
oauth2.Config
|
||||
|
||||
appName string
|
||||
webhookSecret string
|
||||
secret []byte
|
||||
appID int64
|
||||
}
|
||||
|
||||
func NewGithubAppConf(cfg *oauth.Config, appName, appSecretPath, appWebhookSecret, appID string) (*GithubAppConf, error) {
|
||||
intAppID, err := strconv.ParseInt(appID, 10, 64)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
appSecret, err := ioutil.ReadFile(appSecretPath)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not read github app secret: %s", err)
|
||||
}
|
||||
|
||||
return &GithubAppConf{
|
||||
appName: appName,
|
||||
webhookSecret: appWebhookSecret,
|
||||
secret: appSecret,
|
||||
appID: intAppID,
|
||||
Config: oauth2.Config{
|
||||
ClientID: cfg.ClientID,
|
||||
ClientSecret: cfg.ClientSecret,
|
||||
Endpoint: oauth2.Endpoint{
|
||||
AuthURL: GithubAuthURL,
|
||||
TokenURL: GithubTokenURL,
|
||||
},
|
||||
RedirectURL: cfg.BaseURL + "/api/v1/users/github/callback",
|
||||
Scopes: cfg.Scopes,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (g *GithubAppConf) GetGithubClient(installationID int64) (*githubsdk.Client, error) {
|
||||
itr, err := ghinstallation.New(
|
||||
http.DefaultTransport,
|
||||
g.appID,
|
||||
installationID,
|
||||
g.secret,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return githubsdk.NewClient(&http.Client{Transport: itr}), nil
|
||||
}
|
||||
|
||||
func (g *GithubAppConf) GetWebhookSecret() string {
|
||||
return g.webhookSecret
|
||||
}
|
||||
|
||||
func (g *GithubAppConf) GetAppName() string {
|
||||
return g.appName
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
githubsdk "github.com/google/go-github/v57/github"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/internal/integrations/vcs"
|
||||
)
|
||||
|
||||
type ghPullRequest struct {
|
||||
repoOwner, repoName string
|
||||
pr *githubsdk.PullRequest
|
||||
}
|
||||
|
||||
func ToVCSRepositoryPullRequest(repoOwner, repoName string, pr *githubsdk.PullRequest) vcs.VCSRepositoryPullRequest {
|
||||
return &ghPullRequest{repoOwner, repoName, pr}
|
||||
}
|
||||
|
||||
func (g *ghPullRequest) GetRepoOwner() string {
|
||||
return g.repoOwner
|
||||
}
|
||||
|
||||
func (g *ghPullRequest) GetRepoName() string {
|
||||
return g.repoName
|
||||
}
|
||||
|
||||
func (g *ghPullRequest) GetVCSID() vcs.VCSObjectID {
|
||||
return vcs.NewVCSObjectInt(g.pr.GetID())
|
||||
}
|
||||
|
||||
func (g *ghPullRequest) GetPRNumber() int64 {
|
||||
return int64(g.pr.GetNumber())
|
||||
}
|
||||
|
||||
func (g *ghPullRequest) GetBaseSHA() string {
|
||||
return g.pr.GetBase().GetSHA()
|
||||
}
|
||||
|
||||
func (g *ghPullRequest) GetHeadSHA() string {
|
||||
return g.pr.GetHead().GetSHA()
|
||||
}
|
||||
|
||||
func (g *ghPullRequest) GetBaseBranch() string {
|
||||
return g.pr.GetBase().GetRef()
|
||||
}
|
||||
|
||||
func (g *ghPullRequest) GetHeadBranch() string {
|
||||
return g.pr.GetHead().GetRef()
|
||||
}
|
||||
|
||||
func (g *ghPullRequest) GetTitle() string {
|
||||
return g.pr.GetTitle()
|
||||
}
|
||||
|
||||
func (g *ghPullRequest) GetState() string {
|
||||
return g.pr.GetState()
|
||||
}
|
||||
|
||||
func createNewBranch(
|
||||
client *githubsdk.Client,
|
||||
gitRepoOwner, gitRepoName, baseBranch, headBranch string,
|
||||
) error {
|
||||
_, resp, err := client.Repositories.GetBranch(
|
||||
context.Background(), gitRepoOwner, gitRepoName, headBranch, 2,
|
||||
)
|
||||
|
||||
headBranchRef := fmt.Sprintf("refs/heads/%s", headBranch)
|
||||
|
||||
if err == nil {
|
||||
return fmt.Errorf("branch %s already exists", headBranch)
|
||||
} else if resp.StatusCode != http.StatusNotFound {
|
||||
return err
|
||||
}
|
||||
|
||||
base, _, err := client.Repositories.GetBranch(
|
||||
context.Background(), gitRepoOwner, gitRepoName, baseBranch, 2,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _, err = client.Git.CreateRef(
|
||||
context.Background(), gitRepoOwner, gitRepoName, &githubsdk.Reference{
|
||||
Ref: githubsdk.String(headBranchRef),
|
||||
Object: &githubsdk.GitObject{
|
||||
SHA: base.Commit.SHA,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func commitFiles(
|
||||
client *githubsdk.Client,
|
||||
files map[string][]byte,
|
||||
gitRepoOwner, gitRepoName, branch string,
|
||||
) error {
|
||||
for filepath, contents := range files {
|
||||
sha := ""
|
||||
|
||||
// get contents of a file if it exists
|
||||
fileData, _, _, _ := client.Repositories.GetContents(
|
||||
context.TODO(),
|
||||
gitRepoOwner,
|
||||
gitRepoName,
|
||||
filepath,
|
||||
&githubsdk.RepositoryContentGetOptions{
|
||||
Ref: branch,
|
||||
},
|
||||
)
|
||||
|
||||
if fileData != nil {
|
||||
sha = *fileData.SHA
|
||||
}
|
||||
|
||||
opts := &githubsdk.RepositoryContentFileOptions{
|
||||
Message: githubsdk.String(fmt.Sprintf("Create %s file", filepath)),
|
||||
Content: contents,
|
||||
Branch: githubsdk.String(branch),
|
||||
SHA: &sha,
|
||||
}
|
||||
|
||||
opts.Committer = &githubsdk.CommitAuthor{
|
||||
Name: githubsdk.String("Hatchet Bot"),
|
||||
Email: githubsdk.String("contact@hatchet.run"),
|
||||
}
|
||||
|
||||
_, _, err := client.Repositories.UpdateFile(
|
||||
context.TODO(),
|
||||
gitRepoOwner,
|
||||
gitRepoName,
|
||||
filepath,
|
||||
opts,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
package vcs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
)
|
||||
|
||||
type VCSRepositoryKind string
|
||||
|
||||
const (
|
||||
// enumeration of in-tree repository kinds
|
||||
VCSRepositoryKindGithub VCSRepositoryKind = "github"
|
||||
VCSRepositoryKindGitlab VCSRepositoryKind = "gitlab"
|
||||
)
|
||||
|
||||
type VCSCheckRunStatus string
|
||||
|
||||
const (
|
||||
VCSCheckRunStatusQueued VCSCheckRunStatus = "queued"
|
||||
VCSCheckRunStatusInProgress VCSCheckRunStatus = "in_progress"
|
||||
VCSCheckRunStatusCompleted VCSCheckRunStatus = "completed"
|
||||
)
|
||||
|
||||
type VCSCheckRunConclusion string
|
||||
|
||||
const (
|
||||
VCSCheckRunConclusionSuccess VCSCheckRunConclusion = "success"
|
||||
VCSCheckRunConclusionFailure VCSCheckRunConclusion = "failure"
|
||||
VCSCheckRunConclusionCancelled VCSCheckRunConclusion = "cancelled"
|
||||
VCSCheckRunConclusionSkipped VCSCheckRunConclusion = "skipped"
|
||||
VCSCheckRunConclusionTimedOut VCSCheckRunConclusion = "timed_out"
|
||||
VCSCheckRunConclusionActionRequired VCSCheckRunConclusion = "action_required"
|
||||
)
|
||||
|
||||
type VCSCheckRun struct {
|
||||
Name string
|
||||
Status VCSCheckRunStatus
|
||||
Conclusion VCSCheckRunConclusion
|
||||
}
|
||||
|
||||
type DirectoryItem struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type CreatePullRequestOpts struct {
|
||||
GitRepoOwner string
|
||||
GitRepoName string
|
||||
|
||||
// (optional) the base branch. If not specified, the default branch is used
|
||||
BaseBranch *string
|
||||
Title string
|
||||
HeadBranchName string
|
||||
Files map[string][]byte
|
||||
}
|
||||
|
||||
// VCSRepository provides an interface to implement for new version control providers
|
||||
// (github, gitlab, etc)
|
||||
type VCSRepository interface {
|
||||
// GetKind returns the kind of VCS provider -- used for downstream integrations
|
||||
GetKind() VCSRepositoryKind
|
||||
|
||||
// GetRepoOwner returns the owner of the repository
|
||||
GetRepoOwner() string
|
||||
|
||||
// GetRepoName returns the name of the repository
|
||||
GetRepoName() string
|
||||
|
||||
// SetupRepository sets up a VCS repository on Hatchet.
|
||||
SetupRepository(teamID string) error
|
||||
|
||||
// GetArchiveLink returns an archive link for a specific repo SHA
|
||||
GetArchiveLink(ref string) (*url.URL, error)
|
||||
|
||||
// GetBranch gets a full branch (name and sha)
|
||||
GetBranch(name string) (VCSBranch, error)
|
||||
|
||||
// CreateOrUpdatePullRequest creates a new pull request or updates an existing one
|
||||
CreateOrUpdatePullRequest(tenantId, workflowRunId string, opts *CreatePullRequestOpts) (*db.GithubPullRequestModel, error)
|
||||
|
||||
// ReadFile returns a file by a SHA reference or path
|
||||
ReadFile(ref, path string) (io.ReadCloser, error)
|
||||
|
||||
// ReadDirectory returns a list of directory items
|
||||
ReadDirectory(ref, path string) ([]DirectoryItem, error)
|
||||
|
||||
// CompareCommits compares a base commit with a head commit
|
||||
CompareCommits(base, head string) (VCSCommitsComparison, error)
|
||||
}
|
||||
|
||||
// VCSObjectID is a generic method for retrieving IDs from the underlying VCS repository.
|
||||
// Depending on the provider, object IDs may be int64 or strings.
|
||||
//
|
||||
// Object ids are meant to be passed between methods in the same VCS repository, so they should
|
||||
// only be read by the same VCS repository that wrote them.
|
||||
type VCSObjectID interface {
|
||||
GetIDString() *string
|
||||
GetIDInt64() *int64
|
||||
}
|
||||
|
||||
type vcsObjectString struct {
|
||||
id string
|
||||
}
|
||||
|
||||
func NewVCSObjectString(id string) VCSObjectID {
|
||||
return vcsObjectString{id}
|
||||
}
|
||||
|
||||
func (v vcsObjectString) GetIDString() *string {
|
||||
return &v.id
|
||||
}
|
||||
|
||||
func (v vcsObjectString) GetIDInt64() *int64 {
|
||||
return nil
|
||||
}
|
||||
|
||||
type vcsObjectInt struct {
|
||||
id int64
|
||||
}
|
||||
|
||||
func NewVCSObjectInt(id int64) VCSObjectID {
|
||||
return vcsObjectInt{id}
|
||||
}
|
||||
|
||||
func (v vcsObjectInt) GetIDString() *string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v vcsObjectInt) GetIDInt64() *int64 {
|
||||
return &v.id
|
||||
}
|
||||
|
||||
type VCSProvider interface {
|
||||
// GetVCSRepositoryFromModule returns the corresponding VCS repository for the module.
|
||||
// Callers should likely use the package method GetVCSProviderFromModule.
|
||||
GetVCSRepositoryFromWorkflow(workflow *db.WorkflowModel) (VCSRepository, error)
|
||||
}
|
||||
|
||||
// GetVCSRepositoryFromWorkflow returns the corresponding VCS repository for the workflow
|
||||
func GetVCSRepositoryFromWorkflow(allProviders map[VCSRepositoryKind]VCSProvider, workflow *db.WorkflowModel) (VCSRepository, error) {
|
||||
var repoKind VCSRepositoryKind
|
||||
|
||||
if deploymentConf, ok := workflow.DeploymentConfig(); ok {
|
||||
if installationId, ok := deploymentConf.GithubAppInstallationID(); ok && installationId != "" {
|
||||
repoKind = VCSRepositoryKindGithub
|
||||
}
|
||||
}
|
||||
|
||||
provider, exists := allProviders[repoKind]
|
||||
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("VCS provider kind '%s' is not registered on this Hatchet instance", repoKind)
|
||||
}
|
||||
|
||||
return provider.GetVCSRepositoryFromWorkflow(workflow)
|
||||
}
|
||||
|
||||
// VCSRepositoryPullRequest abstracts the underlying pull or merge request methods to only
|
||||
// extract relevant information.
|
||||
type VCSRepositoryPullRequest interface {
|
||||
GetRepoOwner() string
|
||||
GetVCSID() VCSObjectID
|
||||
GetPRNumber() int64
|
||||
GetRepoName() string
|
||||
GetBaseSHA() string
|
||||
GetHeadSHA() string
|
||||
GetBaseBranch() string
|
||||
GetHeadBranch() string
|
||||
GetTitle() string
|
||||
GetState() string
|
||||
}
|
||||
|
||||
type VCSBranch interface {
|
||||
GetName() string
|
||||
GetLatestRef() string
|
||||
}
|
||||
|
||||
type VCSCommitsComparison interface {
|
||||
GetFiles() []CommitFile
|
||||
}
|
||||
|
||||
type CommitFile struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (f CommitFile) GetFilename() string {
|
||||
return f.Name
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package zip
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ZIPDownloader struct {
|
||||
SourceURL string
|
||||
|
||||
ZipFolderDest string
|
||||
ZipName string
|
||||
AssetFolderDest string
|
||||
|
||||
RemoveAfterDownload bool
|
||||
}
|
||||
|
||||
func (z *ZIPDownloader) DownloadToFile() error {
|
||||
// Get the data
|
||||
resp, err := http.Get(z.SourceURL)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Create the file
|
||||
out, err := os.Create(filepath.Join(z.ZipFolderDest, z.ZipName))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer out.Close()
|
||||
|
||||
// Write the body to file
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (z *ZIPDownloader) UnzipToDir() error {
|
||||
r, err := zip.OpenReader(filepath.Join(z.ZipFolderDest, z.ZipName))
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer r.Close()
|
||||
|
||||
for _, f := range r.File {
|
||||
// Store filename/path for returning and using later on
|
||||
fpath := filepath.Join(z.AssetFolderDest, f.Name) // nolint: gosec
|
||||
|
||||
// Check for ZipSlip. More Info: http://bit.ly/2MsjAWE
|
||||
if !strings.HasPrefix(fpath, filepath.Clean(z.AssetFolderDest)+string(os.PathSeparator)) {
|
||||
return fmt.Errorf("%s: illegal file path", fpath)
|
||||
}
|
||||
|
||||
if f.FileInfo().IsDir() {
|
||||
// Make Folder
|
||||
os.MkdirAll(fpath, os.ModePerm) // nolint: errcheck
|
||||
continue
|
||||
}
|
||||
|
||||
// delete file if exists
|
||||
os.Remove(fpath)
|
||||
|
||||
// Make File
|
||||
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rc, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(outFile, rc) // nolint: gosec
|
||||
|
||||
// Close the file without defer to close before next iteration of loop
|
||||
outFile.Close()
|
||||
rc.Close()
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if z.RemoveAfterDownload {
|
||||
os.Remove(filepath.Join(z.ZipFolderDest, z.ZipName))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/steebchen/prisma-client-go/runtime/types"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/internal/encryption"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
)
|
||||
|
||||
type CreateInstallationOpts struct {
|
||||
// (required) the installation id
|
||||
InstallationID int `validate:"required"`
|
||||
|
||||
// (required) the account ID
|
||||
AccountID int `validate:"required"`
|
||||
|
||||
// (required) the account name
|
||||
AccountName string `validate:"required"`
|
||||
|
||||
// (optional) the account avatar URL
|
||||
AccountAvatarURL *string
|
||||
|
||||
// (optional) the installation settings URL
|
||||
InstallationSettingsURL *string
|
||||
|
||||
// (optional) the installation configuration
|
||||
Config *types.JSON
|
||||
}
|
||||
|
||||
type CreateGithubWebhookOpts struct {
|
||||
// (required) the repo owner
|
||||
RepoOwner string `validate:"required"`
|
||||
|
||||
// (required) the repo name
|
||||
RepoName string `validate:"required"`
|
||||
|
||||
// (required) the signing secret
|
||||
SigningSecret []byte `validate:"required,min=1"`
|
||||
}
|
||||
|
||||
func NewGithubWebhookCreateOpts(
|
||||
enc encryption.EncryptionService,
|
||||
repoOwner string,
|
||||
repoName string,
|
||||
) (opts *CreateGithubWebhookOpts, signingSecret string, err error) {
|
||||
signingSecret, err = encryption.GenerateRandomBytes(16)
|
||||
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to generate signing secret: %s", err.Error())
|
||||
}
|
||||
|
||||
// use the encryption service to encrypt the access and refresh token
|
||||
signingSecretEncrypted, err := enc.Encrypt([]byte(signingSecret), "github_signing_secret")
|
||||
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("failed to encrypt access token: %s", err.Error())
|
||||
}
|
||||
|
||||
opts = &CreateGithubWebhookOpts{
|
||||
RepoOwner: repoOwner,
|
||||
RepoName: repoName,
|
||||
SigningSecret: signingSecretEncrypted,
|
||||
}
|
||||
|
||||
return opts, signingSecret, nil
|
||||
}
|
||||
|
||||
type CreateGithubAppOAuthOpts struct {
|
||||
// (required) the oauth provider's user id
|
||||
GithubUserID int `validate:"required"`
|
||||
|
||||
// (required) the oauth provider's access token
|
||||
AccessToken []byte `validate:"required,min=1"`
|
||||
|
||||
// (optional) the oauth provider's refresh token
|
||||
RefreshToken *[]byte
|
||||
|
||||
// (optional) the oauth provider's expiry time
|
||||
ExpiresAt *time.Time
|
||||
|
||||
// (optional) the oauth provider's configuration
|
||||
Config *types.JSON
|
||||
}
|
||||
|
||||
type UpdateInstallationOpts struct {
|
||||
}
|
||||
|
||||
type UpdatePullRequestOpts struct {
|
||||
Title *string
|
||||
|
||||
State *string
|
||||
|
||||
HeadBranch *string
|
||||
|
||||
BaseBranch *string
|
||||
}
|
||||
|
||||
type GithubRepository interface {
|
||||
CreateInstallation(githubUserId int, opts *CreateInstallationOpts) (*db.GithubAppInstallationModel, error)
|
||||
|
||||
AddGithubUserIdToInstallation(installationID, accountID, githubUserId int) (*db.GithubAppInstallationModel, error)
|
||||
|
||||
ReadGithubAppInstallationByID(installationId string) (*db.GithubAppInstallationModel, error)
|
||||
|
||||
ReadGithubWebhookById(id string) (*db.GithubWebhookModel, error)
|
||||
|
||||
ReadGithubWebhook(tenantId, repoOwner, repoName string) (*db.GithubWebhookModel, error)
|
||||
|
||||
ReadGithubAppOAuthByGithubUserID(githubUserID int) (*db.GithubAppOAuthModel, error)
|
||||
|
||||
CanUserAccessInstallation(installationId, userId string) (bool, error)
|
||||
|
||||
ReadGithubAppInstallationByInstallationAndAccountID(installationID, accountID int) (*db.GithubAppInstallationModel, error)
|
||||
|
||||
CreateGithubWebhook(tenantId string, opts *CreateGithubWebhookOpts) (*db.GithubWebhookModel, error)
|
||||
|
||||
UpsertGithubAppOAuth(userId string, opts *CreateGithubAppOAuthOpts) (*db.GithubAppOAuthModel, error)
|
||||
|
||||
DeleteInstallation(installationId, accountId int) (*db.GithubAppInstallationModel, error)
|
||||
|
||||
ListGithubAppInstallationsByUserID(userId string) ([]db.GithubAppInstallationModel, error)
|
||||
|
||||
UpdatePullRequest(tenantId, prId string, opts *UpdatePullRequestOpts) (*db.GithubPullRequestModel, error)
|
||||
|
||||
GetPullRequest(tenantId, repoOwner, repoName string, prNumber int) (*db.GithubPullRequestModel, error)
|
||||
}
|
||||
@@ -232,6 +232,47 @@ func (ns NullTenantMemberRole) Value() (driver.Value, error) {
|
||||
return string(ns.TenantMemberRole), nil
|
||||
}
|
||||
|
||||
type VcsProvider string
|
||||
|
||||
const (
|
||||
VcsProviderGITHUB VcsProvider = "GITHUB"
|
||||
)
|
||||
|
||||
func (e *VcsProvider) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = VcsProvider(s)
|
||||
case string:
|
||||
*e = VcsProvider(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for VcsProvider: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullVcsProvider struct {
|
||||
VcsProvider VcsProvider `json:"VcsProvider"`
|
||||
Valid bool `json:"valid"` // Valid is true if VcsProvider is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullVcsProvider) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.VcsProvider, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.VcsProvider.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullVcsProvider) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.VcsProvider), nil
|
||||
}
|
||||
|
||||
type WorkerStatus string
|
||||
|
||||
const (
|
||||
@@ -383,6 +424,86 @@ type GetGroupKeyRun struct {
|
||||
WorkflowRunId pgtype.UUID `json:"workflowRunId"`
|
||||
}
|
||||
|
||||
type GithubAppInstallation struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
|
||||
DeletedAt pgtype.Timestamp `json:"deletedAt"`
|
||||
GithubAppOAuthId pgtype.UUID `json:"githubAppOAuthId"`
|
||||
InstallationId int32 `json:"installationId"`
|
||||
AccountName string `json:"accountName"`
|
||||
AccountId int32 `json:"accountId"`
|
||||
AccountAvatarURL pgtype.Text `json:"accountAvatarURL"`
|
||||
InstallationSettingsURL pgtype.Text `json:"installationSettingsURL"`
|
||||
Config []byte `json:"config"`
|
||||
TenantId pgtype.UUID `json:"tenantId"`
|
||||
TenantVcsProviderId pgtype.UUID `json:"tenantVcsProviderId"`
|
||||
}
|
||||
|
||||
type GithubAppInstallationToGithubWebhook struct {
|
||||
A pgtype.UUID `json:"A"`
|
||||
B pgtype.UUID `json:"B"`
|
||||
}
|
||||
|
||||
type GithubAppOAuth struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
|
||||
DeletedAt pgtype.Timestamp `json:"deletedAt"`
|
||||
GithubUserID int32 `json:"githubUserID"`
|
||||
AccessToken []byte `json:"accessToken"`
|
||||
RefreshToken []byte `json:"refreshToken"`
|
||||
ExpiresAt pgtype.Timestamp `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type GithubAppOAuthToUser struct {
|
||||
A pgtype.UUID `json:"A"`
|
||||
B pgtype.UUID `json:"B"`
|
||||
}
|
||||
|
||||
type GithubPullRequest struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
|
||||
DeletedAt pgtype.Timestamp `json:"deletedAt"`
|
||||
TenantId pgtype.UUID `json:"tenantId"`
|
||||
RepositoryOwner string `json:"repositoryOwner"`
|
||||
RepositoryName string `json:"repositoryName"`
|
||||
PullRequestID int32 `json:"pullRequestID"`
|
||||
PullRequestTitle string `json:"pullRequestTitle"`
|
||||
PullRequestNumber int32 `json:"pullRequestNumber"`
|
||||
PullRequestHeadBranch string `json:"pullRequestHeadBranch"`
|
||||
PullRequestBaseBranch string `json:"pullRequestBaseBranch"`
|
||||
PullRequestState string `json:"pullRequestState"`
|
||||
}
|
||||
|
||||
type GithubPullRequestComment struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
|
||||
DeletedAt pgtype.Timestamp `json:"deletedAt"`
|
||||
TenantId pgtype.UUID `json:"tenantId"`
|
||||
PullRequestID pgtype.UUID `json:"pullRequestID"`
|
||||
ModuleID string `json:"moduleID"`
|
||||
CommentID int32 `json:"commentID"`
|
||||
}
|
||||
|
||||
type GithubPullRequestToWorkflowRun struct {
|
||||
A pgtype.UUID `json:"A"`
|
||||
B pgtype.UUID `json:"B"`
|
||||
}
|
||||
|
||||
type GithubWebhook struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
|
||||
DeletedAt pgtype.Timestamp `json:"deletedAt"`
|
||||
TenantId pgtype.UUID `json:"tenantId"`
|
||||
RepositoryOwner string `json:"repositoryOwner"`
|
||||
RepositoryName string `json:"repositoryName"`
|
||||
SigningSecret []byte `json:"signingSecret"`
|
||||
}
|
||||
|
||||
type Job struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
@@ -481,6 +602,8 @@ type StepRun struct {
|
||||
CancelledReason pgtype.Text `json:"cancelledReason"`
|
||||
CancelledError pgtype.Text `json:"cancelledError"`
|
||||
InputSchema []byte `json:"inputSchema"`
|
||||
CallerFiles []byte `json:"callerFiles"`
|
||||
GitRepoBranch pgtype.Text `json:"gitRepoBranch"`
|
||||
}
|
||||
|
||||
type StepRunOrder struct {
|
||||
@@ -488,6 +611,24 @@ type StepRunOrder struct {
|
||||
B pgtype.UUID `json:"B"`
|
||||
}
|
||||
|
||||
type StepRunResultArchive struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
|
||||
DeletedAt pgtype.Timestamp `json:"deletedAt"`
|
||||
StepRunId pgtype.UUID `json:"stepRunId"`
|
||||
Order int16 `json:"order"`
|
||||
Input []byte `json:"input"`
|
||||
Output []byte `json:"output"`
|
||||
Error pgtype.Text `json:"error"`
|
||||
StartedAt pgtype.Timestamp `json:"startedAt"`
|
||||
FinishedAt pgtype.Timestamp `json:"finishedAt"`
|
||||
TimeoutAt pgtype.Timestamp `json:"timeoutAt"`
|
||||
CancelledAt pgtype.Timestamp `json:"cancelledAt"`
|
||||
CancelledReason pgtype.Text `json:"cancelledReason"`
|
||||
CancelledError pgtype.Text `json:"cancelledError"`
|
||||
}
|
||||
|
||||
type Tenant struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
@@ -518,6 +659,16 @@ type TenantMember struct {
|
||||
Role TenantMemberRole `json:"role"`
|
||||
}
|
||||
|
||||
type TenantVcsProvider struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
|
||||
DeletedAt pgtype.Timestamp `json:"deletedAt"`
|
||||
TenantId pgtype.UUID `json:"tenantId"`
|
||||
VcsProvider VcsProvider `json:"vcsProvider"`
|
||||
Config []byte `json:"config"`
|
||||
}
|
||||
|
||||
type Ticker struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
@@ -594,6 +745,18 @@ type WorkflowConcurrency struct {
|
||||
LimitStrategy ConcurrencyLimitStrategy `json:"limitStrategy"`
|
||||
}
|
||||
|
||||
type WorkflowDeploymentConfig struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
|
||||
DeletedAt pgtype.Timestamp `json:"deletedAt"`
|
||||
WorkflowId pgtype.UUID `json:"workflowId"`
|
||||
GitRepoName string `json:"gitRepoName"`
|
||||
GitRepoOwner string `json:"gitRepoOwner"`
|
||||
GitRepoBranch string `json:"gitRepoBranch"`
|
||||
GithubAppInstallationId pgtype.UUID `json:"githubAppInstallationId"`
|
||||
}
|
||||
|
||||
type WorkflowRun struct {
|
||||
CreatedAt pgtype.Timestamp `json:"createdAt"`
|
||||
UpdatedAt pgtype.Timestamp `json:"updatedAt"`
|
||||
@@ -607,6 +770,7 @@ type WorkflowRun struct {
|
||||
ConcurrencyGroupId pgtype.Text `json:"concurrencyGroupId"`
|
||||
DisplayName pgtype.Text `json:"displayName"`
|
||||
ID pgtype.UUID `json:"id"`
|
||||
GitRepoBranch pgtype.Text `json:"gitRepoBranch"`
|
||||
}
|
||||
|
||||
type WorkflowRunTriggeredBy struct {
|
||||
|
||||
@@ -13,11 +13,14 @@ CREATE TYPE "StepRunStatus" AS ENUM ('PENDING', 'PENDING_ASSIGNMENT', 'ASSIGNED'
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TenantMemberRole" AS ENUM ('OWNER', 'ADMIN', 'MEMBER');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "VcsProvider" AS ENUM ('GITHUB');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "WorkerStatus" AS ENUM ('ACTIVE', 'INACTIVE');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "WorkflowRunStatus" AS ENUM ('PENDING', 'QUEUED', 'RUNNING', 'SUCCEEDED', 'FAILED');
|
||||
CREATE TYPE "WorkflowRunStatus" AS ENUM ('PENDING', 'RUNNING', 'SUCCEEDED', 'FAILED', 'QUEUED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "APIToken" (
|
||||
@@ -34,10 +37,10 @@ CREATE TABLE "APIToken" (
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Action" (
|
||||
"id" UUID NOT NULL,
|
||||
"actionId" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"tenantId" UUID NOT NULL,
|
||||
"actionId" TEXT NOT NULL,
|
||||
"id" UUID NOT NULL,
|
||||
|
||||
CONSTRAINT "Action_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
@@ -75,7 +78,6 @@ CREATE TABLE "GetGroupKeyRun" (
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"workflowRunId" UUID NOT NULL,
|
||||
"workerId" UUID,
|
||||
"tickerId" UUID,
|
||||
"status" "StepRunStatus" NOT NULL DEFAULT 'PENDING',
|
||||
@@ -89,10 +91,91 @@ CREATE TABLE "GetGroupKeyRun" (
|
||||
"cancelledAt" TIMESTAMP(3),
|
||||
"cancelledReason" TEXT,
|
||||
"cancelledError" TEXT,
|
||||
"workflowRunId" UUID NOT NULL,
|
||||
|
||||
CONSTRAINT "GetGroupKeyRun_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GithubAppInstallation" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"githubAppOAuthId" UUID NOT NULL,
|
||||
"installationId" INTEGER NOT NULL,
|
||||
"accountName" TEXT NOT NULL,
|
||||
"accountId" INTEGER NOT NULL,
|
||||
"accountAvatarURL" TEXT,
|
||||
"installationSettingsURL" TEXT,
|
||||
"config" JSONB,
|
||||
"tenantId" UUID,
|
||||
"tenantVcsProviderId" UUID,
|
||||
|
||||
CONSTRAINT "GithubAppInstallation_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GithubAppOAuth" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"githubUserID" INTEGER NOT NULL,
|
||||
"accessToken" BYTEA NOT NULL,
|
||||
"refreshToken" BYTEA,
|
||||
"expiresAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "GithubAppOAuth_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GithubPullRequest" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"repositoryOwner" TEXT NOT NULL,
|
||||
"repositoryName" TEXT NOT NULL,
|
||||
"pullRequestID" INTEGER NOT NULL,
|
||||
"pullRequestTitle" TEXT NOT NULL,
|
||||
"pullRequestNumber" INTEGER NOT NULL,
|
||||
"pullRequestHeadBranch" TEXT NOT NULL,
|
||||
"pullRequestBaseBranch" TEXT NOT NULL,
|
||||
"pullRequestState" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "GithubPullRequest_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GithubPullRequestComment" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"pullRequestID" UUID NOT NULL,
|
||||
"moduleID" TEXT NOT NULL,
|
||||
"commentID" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "GithubPullRequestComment_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GithubWebhook" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"repositoryOwner" TEXT NOT NULL,
|
||||
"repositoryName" TEXT NOT NULL,
|
||||
"signingSecret" BYTEA NOT NULL,
|
||||
|
||||
CONSTRAINT "GithubWebhook_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Job" (
|
||||
"id" UUID NOT NULL,
|
||||
@@ -115,7 +198,6 @@ CREATE TABLE "JobRun" (
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"workflowRunId" UUID NOT NULL,
|
||||
"jobId" UUID NOT NULL,
|
||||
"tickerId" UUID,
|
||||
"status" "JobRunStatus" NOT NULL DEFAULT 'PENDING',
|
||||
@@ -126,6 +208,7 @@ CREATE TABLE "JobRun" (
|
||||
"cancelledAt" TIMESTAMP(3),
|
||||
"cancelledReason" TEXT,
|
||||
"cancelledError" TEXT,
|
||||
"workflowRunId" UUID NOT NULL,
|
||||
|
||||
CONSTRAINT "JobRun_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
@@ -196,10 +279,34 @@ CREATE TABLE "StepRun" (
|
||||
"cancelledAt" TIMESTAMP(3),
|
||||
"cancelledReason" TEXT,
|
||||
"cancelledError" TEXT,
|
||||
"inputSchema" JSONB,
|
||||
"callerFiles" JSONB,
|
||||
"gitRepoBranch" TEXT,
|
||||
|
||||
CONSTRAINT "StepRun_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "StepRunResultArchive" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"stepRunId" UUID NOT NULL,
|
||||
"order" SMALLSERIAL NOT NULL,
|
||||
"input" JSONB,
|
||||
"output" JSONB,
|
||||
"error" TEXT,
|
||||
"startedAt" TIMESTAMP(3),
|
||||
"finishedAt" TIMESTAMP(3),
|
||||
"timeoutAt" TIMESTAMP(3),
|
||||
"cancelledAt" TIMESTAMP(3),
|
||||
"cancelledReason" TEXT,
|
||||
"cancelledError" TEXT,
|
||||
|
||||
CONSTRAINT "StepRunResultArchive_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Tenant" (
|
||||
"id" UUID NOT NULL,
|
||||
@@ -239,6 +346,19 @@ CREATE TABLE "TenantMember" (
|
||||
CONSTRAINT "TenantMember_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "TenantVcsProvider" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"vcsProvider" "VcsProvider" NOT NULL,
|
||||
"config" JSONB,
|
||||
|
||||
CONSTRAINT "TenantVcsProvider_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Ticker" (
|
||||
"id" UUID NOT NULL,
|
||||
@@ -271,9 +391,9 @@ CREATE TABLE "UserOAuth" (
|
||||
"userId" UUID NOT NULL,
|
||||
"provider" TEXT NOT NULL,
|
||||
"providerUserId" TEXT NOT NULL,
|
||||
"expiresAt" TIMESTAMP(3),
|
||||
"accessToken" BYTEA NOT NULL,
|
||||
"refreshToken" BYTEA,
|
||||
"expiresAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "UserOAuth_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
@@ -338,19 +458,35 @@ CREATE TABLE "WorkflowConcurrency" (
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "WorkflowRun" (
|
||||
CREATE TABLE "WorkflowDeploymentConfig" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"displayName" TEXT,
|
||||
"workflowId" UUID NOT NULL,
|
||||
"gitRepoName" TEXT NOT NULL,
|
||||
"gitRepoOwner" TEXT NOT NULL,
|
||||
"gitRepoBranch" TEXT NOT NULL,
|
||||
"githubAppInstallationId" UUID,
|
||||
|
||||
CONSTRAINT "WorkflowDeploymentConfig_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "WorkflowRun" (
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"workflowVersionId" UUID NOT NULL,
|
||||
"concurrencyGroupId" TEXT,
|
||||
"status" "WorkflowRunStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"error" TEXT,
|
||||
"startedAt" TIMESTAMP(3),
|
||||
"finishedAt" TIMESTAMP(3),
|
||||
"concurrencyGroupId" TEXT,
|
||||
"displayName" TEXT,
|
||||
"id" UUID NOT NULL,
|
||||
"gitRepoBranch" TEXT,
|
||||
|
||||
CONSTRAINT "WorkflowRun_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
@@ -362,12 +498,12 @@ CREATE TABLE "WorkflowRunTriggeredBy" (
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"parentId" UUID NOT NULL,
|
||||
"input" JSONB,
|
||||
"eventId" UUID,
|
||||
"cronParentId" UUID,
|
||||
"cronSchedule" TEXT,
|
||||
"scheduledId" UUID,
|
||||
"input" JSONB,
|
||||
"parentId" UUID NOT NULL,
|
||||
|
||||
CONSTRAINT "WorkflowRunTriggeredBy_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
@@ -427,16 +563,34 @@ CREATE TABLE "WorkflowVersion" (
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"checksum" TEXT NOT NULL,
|
||||
"version" TEXT,
|
||||
"order" SMALLSERIAL NOT NULL,
|
||||
"workflowId" UUID NOT NULL,
|
||||
"checksum" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "WorkflowVersion_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_ActionToWorker" (
|
||||
"B" UUID NOT NULL,
|
||||
"A" UUID NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_GithubAppInstallationToGithubWebhook" (
|
||||
"A" UUID NOT NULL,
|
||||
"B" UUID NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_GithubAppOAuthToUser" (
|
||||
"A" UUID NOT NULL,
|
||||
"B" UUID NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_GithubPullRequestToWorkflowRun" (
|
||||
"A" UUID NOT NULL,
|
||||
"B" UUID NOT NULL
|
||||
);
|
||||
@@ -486,6 +640,33 @@ CREATE UNIQUE INDEX "GetGroupKeyRun_id_key" ON "GetGroupKeyRun"("id" ASC);
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GetGroupKeyRun_workflowRunId_key" ON "GetGroupKeyRun"("workflowRunId" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubAppInstallation_id_key" ON "GithubAppInstallation"("id" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubAppInstallation_installationId_accountId_key" ON "GithubAppInstallation"("installationId" ASC, "accountId" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubAppOAuth_githubUserID_key" ON "GithubAppOAuth"("githubUserID" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubAppOAuth_id_key" ON "GithubAppOAuth"("id" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubPullRequest_id_key" ON "GithubPullRequest"("id" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubPullRequest_tenantId_repositoryOwner_repositoryName_p_key" ON "GithubPullRequest"("tenantId" ASC, "repositoryOwner" ASC, "repositoryName" ASC, "pullRequestNumber" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubPullRequestComment_id_key" ON "GithubPullRequestComment"("id" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubWebhook_id_key" ON "GithubWebhook"("id" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubWebhook_tenantId_repositoryOwner_repositoryName_key" ON "GithubWebhook"("tenantId" ASC, "repositoryOwner" ASC, "repositoryName" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Job_id_key" ON "Job"("id" ASC);
|
||||
|
||||
@@ -519,6 +700,9 @@ CREATE UNIQUE INDEX "Step_jobId_readableId_key" ON "Step"("jobId" ASC, "readable
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "StepRun_id_key" ON "StepRun"("id" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "StepRunResultArchive_id_key" ON "StepRunResultArchive"("id" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Tenant_id_key" ON "Tenant"("id" ASC);
|
||||
|
||||
@@ -534,6 +718,12 @@ CREATE UNIQUE INDEX "TenantMember_id_key" ON "TenantMember"("id" ASC);
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "TenantMember_tenantId_userId_key" ON "TenantMember"("tenantId" ASC, "userId" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "TenantVcsProvider_id_key" ON "TenantVcsProvider"("id" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "TenantVcsProvider_tenantId_vcsProvider_key" ON "TenantVcsProvider"("tenantId" ASC, "vcsProvider" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Ticker_id_key" ON "Ticker"("id" ASC);
|
||||
|
||||
@@ -573,6 +763,12 @@ CREATE UNIQUE INDEX "WorkflowConcurrency_id_key" ON "WorkflowConcurrency"("id" A
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "WorkflowConcurrency_workflowVersionId_key" ON "WorkflowConcurrency"("workflowVersionId" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "WorkflowDeploymentConfig_id_key" ON "WorkflowDeploymentConfig"("id" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "WorkflowDeploymentConfig_workflowId_key" ON "WorkflowDeploymentConfig"("workflowId" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "WorkflowRun_id_key" ON "WorkflowRun"("id" ASC);
|
||||
|
||||
@@ -615,6 +811,24 @@ CREATE UNIQUE INDEX "_ActionToWorker_AB_unique" ON "_ActionToWorker"("A" ASC, "B
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_ActionToWorker_B_index" ON "_ActionToWorker"("B" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_GithubAppInstallationToGithubWebhook_AB_unique" ON "_GithubAppInstallationToGithubWebhook"("A" ASC, "B" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_GithubAppInstallationToGithubWebhook_B_index" ON "_GithubAppInstallationToGithubWebhook"("B" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_GithubAppOAuthToUser_AB_unique" ON "_GithubAppOAuthToUser"("A" ASC, "B" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_GithubAppOAuthToUser_B_index" ON "_GithubAppOAuthToUser"("B" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_GithubPullRequestToWorkflowRun_AB_unique" ON "_GithubPullRequestToWorkflowRun"("A" ASC, "B" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_GithubPullRequestToWorkflowRun_B_index" ON "_GithubPullRequestToWorkflowRun"("B" ASC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_ServiceToWorker_AB_unique" ON "_ServiceToWorker"("A" ASC, "B" ASC);
|
||||
|
||||
@@ -663,6 +877,27 @@ ALTER TABLE "GetGroupKeyRun" ADD CONSTRAINT "GetGroupKeyRun_workerId_fkey" FOREI
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GetGroupKeyRun" ADD CONSTRAINT "GetGroupKeyRun_workflowRunId_fkey" FOREIGN KEY ("workflowRunId") REFERENCES "WorkflowRun"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubAppInstallation" ADD CONSTRAINT "GithubAppInstallation_githubAppOAuthId_fkey" FOREIGN KEY ("githubAppOAuthId") REFERENCES "GithubAppOAuth"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubAppInstallation" ADD CONSTRAINT "GithubAppInstallation_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubAppInstallation" ADD CONSTRAINT "GithubAppInstallation_tenantVcsProviderId_fkey" FOREIGN KEY ("tenantVcsProviderId") REFERENCES "TenantVcsProvider"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubPullRequest" ADD CONSTRAINT "GithubPullRequest_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubPullRequestComment" ADD CONSTRAINT "GithubPullRequestComment_pullRequestID_fkey" FOREIGN KEY ("pullRequestID") REFERENCES "GithubPullRequest"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubPullRequestComment" ADD CONSTRAINT "GithubPullRequestComment_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubWebhook" ADD CONSTRAINT "GithubWebhook_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Job" ADD CONSTRAINT "Job_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
@@ -714,6 +949,9 @@ ALTER TABLE "StepRun" ADD CONSTRAINT "StepRun_tickerId_fkey" FOREIGN KEY ("ticke
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "StepRun" ADD CONSTRAINT "StepRun_workerId_fkey" FOREIGN KEY ("workerId") REFERENCES "Worker"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "StepRunResultArchive" ADD CONSTRAINT "StepRunResultArchive_stepRunId_fkey" FOREIGN KEY ("stepRunId") REFERENCES "StepRun"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "TenantInviteLink" ADD CONSTRAINT "TenantInviteLink_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
@@ -723,6 +961,9 @@ ALTER TABLE "TenantMember" ADD CONSTRAINT "TenantMember_tenantId_fkey" FOREIGN K
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "TenantMember" ADD CONSTRAINT "TenantMember_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "TenantVcsProvider" ADD CONSTRAINT "TenantVcsProvider_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "UserOAuth" ADD CONSTRAINT "UserOAuth_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
@@ -747,6 +988,12 @@ ALTER TABLE "WorkflowConcurrency" ADD CONSTRAINT "WorkflowConcurrency_getConcurr
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "WorkflowConcurrency" ADD CONSTRAINT "WorkflowConcurrency_workflowVersionId_fkey" FOREIGN KEY ("workflowVersionId") REFERENCES "WorkflowVersion"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "WorkflowDeploymentConfig" ADD CONSTRAINT "WorkflowDeploymentConfig_githubAppInstallationId_fkey" FOREIGN KEY ("githubAppInstallationId") REFERENCES "GithubAppInstallation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "WorkflowDeploymentConfig" ADD CONSTRAINT "WorkflowDeploymentConfig_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "Workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "WorkflowRun" ADD CONSTRAINT "WorkflowRun_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
@@ -801,6 +1048,24 @@ ALTER TABLE "_ActionToWorker" ADD CONSTRAINT "_ActionToWorker_A_fkey" FOREIGN KE
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_ActionToWorker" ADD CONSTRAINT "_ActionToWorker_B_fkey" FOREIGN KEY ("B") REFERENCES "Worker"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubAppInstallationToGithubWebhook" ADD CONSTRAINT "_GithubAppInstallationToGithubWebhook_A_fkey" FOREIGN KEY ("A") REFERENCES "GithubAppInstallation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubAppInstallationToGithubWebhook" ADD CONSTRAINT "_GithubAppInstallationToGithubWebhook_B_fkey" FOREIGN KEY ("B") REFERENCES "GithubWebhook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubAppOAuthToUser" ADD CONSTRAINT "_GithubAppOAuthToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "GithubAppOAuth"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubAppOAuthToUser" ADD CONSTRAINT "_GithubAppOAuthToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubPullRequestToWorkflowRun" ADD CONSTRAINT "_GithubPullRequestToWorkflowRun_A_fkey" FOREIGN KEY ("A") REFERENCES "GithubPullRequest"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubPullRequestToWorkflowRun" ADD CONSTRAINT "_GithubPullRequestToWorkflowRun_B_fkey" FOREIGN KEY ("B") REFERENCES "WorkflowRun"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_ServiceToWorker" ADD CONSTRAINT "_ServiceToWorker_A_fkey" FOREIGN KEY ("A") REFERENCES "Service"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
|
||||
@@ -91,10 +91,11 @@ RETURNING sr.*;
|
||||
|
||||
-- name: UpdateStepRunOverridesData :one
|
||||
UPDATE
|
||||
"StepRun" as sr
|
||||
"StepRun" AS sr
|
||||
SET
|
||||
"updatedAt" = CURRENT_TIMESTAMP,
|
||||
"input" = jsonb_set("input", @fieldPath::text[], @jsonData::jsonb, true)
|
||||
"input" = jsonb_set("input", @fieldPath::text[], @jsonData::jsonb, true),
|
||||
"callerFiles" = jsonb_set("callerFiles", @overridesKey::text[], to_jsonb(@callerFile::text), true)
|
||||
WHERE
|
||||
sr."tenantId" = @tenantId::uuid AND
|
||||
sr."id" = @stepRunId::uuid
|
||||
@@ -109,4 +110,58 @@ SET
|
||||
WHERE
|
||||
sr."tenantId" = @tenantId::uuid AND
|
||||
sr."id" = @stepRunId::uuid
|
||||
RETURNING "inputSchema";
|
||||
RETURNING "inputSchema";
|
||||
|
||||
-- name: ArchiveStepRunResultFromStepRun :one
|
||||
WITH step_run_data AS (
|
||||
SELECT
|
||||
"id" AS step_run_id,
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
"deletedAt",
|
||||
"order",
|
||||
"input",
|
||||
"output",
|
||||
"error",
|
||||
"startedAt",
|
||||
"finishedAt",
|
||||
"timeoutAt",
|
||||
"cancelledAt",
|
||||
"cancelledReason",
|
||||
"cancelledError"
|
||||
FROM "StepRun"
|
||||
WHERE "id" = @stepRunId::uuid AND "tenantId" = @tenantId::uuid
|
||||
)
|
||||
INSERT INTO "StepRunResultArchive" (
|
||||
"id",
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
"deletedAt",
|
||||
"stepRunId",
|
||||
"input",
|
||||
"output",
|
||||
"error",
|
||||
"startedAt",
|
||||
"finishedAt",
|
||||
"timeoutAt",
|
||||
"cancelledAt",
|
||||
"cancelledReason",
|
||||
"cancelledError"
|
||||
)
|
||||
SELECT
|
||||
COALESCE(sqlc.arg('id')::uuid, gen_random_uuid()),
|
||||
CURRENT_TIMESTAMP,
|
||||
CURRENT_TIMESTAMP,
|
||||
step_run_data."deletedAt",
|
||||
step_run_data.step_run_id,
|
||||
step_run_data."input",
|
||||
step_run_data."output",
|
||||
step_run_data."error",
|
||||
step_run_data."startedAt",
|
||||
step_run_data."finishedAt",
|
||||
step_run_data."timeoutAt",
|
||||
step_run_data."cancelledAt",
|
||||
step_run_data."cancelledReason",
|
||||
step_run_data."cancelledError"
|
||||
FROM step_run_data
|
||||
RETURNING *;
|
||||
@@ -11,9 +11,93 @@ import (
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const archiveStepRunResultFromStepRun = `-- name: ArchiveStepRunResultFromStepRun :one
|
||||
WITH step_run_data AS (
|
||||
SELECT
|
||||
"id" AS step_run_id,
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
"deletedAt",
|
||||
"order",
|
||||
"input",
|
||||
"output",
|
||||
"error",
|
||||
"startedAt",
|
||||
"finishedAt",
|
||||
"timeoutAt",
|
||||
"cancelledAt",
|
||||
"cancelledReason",
|
||||
"cancelledError"
|
||||
FROM "StepRun"
|
||||
WHERE "id" = $2::uuid AND "tenantId" = $3::uuid
|
||||
)
|
||||
INSERT INTO "StepRunResultArchive" (
|
||||
"id",
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
"deletedAt",
|
||||
"stepRunId",
|
||||
"input",
|
||||
"output",
|
||||
"error",
|
||||
"startedAt",
|
||||
"finishedAt",
|
||||
"timeoutAt",
|
||||
"cancelledAt",
|
||||
"cancelledReason",
|
||||
"cancelledError"
|
||||
)
|
||||
SELECT
|
||||
COALESCE($1::uuid, gen_random_uuid()),
|
||||
CURRENT_TIMESTAMP,
|
||||
CURRENT_TIMESTAMP,
|
||||
step_run_data."deletedAt",
|
||||
step_run_data.step_run_id,
|
||||
step_run_data."input",
|
||||
step_run_data."output",
|
||||
step_run_data."error",
|
||||
step_run_data."startedAt",
|
||||
step_run_data."finishedAt",
|
||||
step_run_data."timeoutAt",
|
||||
step_run_data."cancelledAt",
|
||||
step_run_data."cancelledReason",
|
||||
step_run_data."cancelledError"
|
||||
FROM step_run_data
|
||||
RETURNING id, "createdAt", "updatedAt", "deletedAt", "stepRunId", "order", input, output, error, "startedAt", "finishedAt", "timeoutAt", "cancelledAt", "cancelledReason", "cancelledError"
|
||||
`
|
||||
|
||||
type ArchiveStepRunResultFromStepRunParams struct {
|
||||
ID pgtype.UUID `json:"id"`
|
||||
Steprunid pgtype.UUID `json:"steprunid"`
|
||||
Tenantid pgtype.UUID `json:"tenantid"`
|
||||
}
|
||||
|
||||
func (q *Queries) ArchiveStepRunResultFromStepRun(ctx context.Context, db DBTX, arg ArchiveStepRunResultFromStepRunParams) (*StepRunResultArchive, error) {
|
||||
row := db.QueryRow(ctx, archiveStepRunResultFromStepRun, arg.ID, arg.Steprunid, arg.Tenantid)
|
||||
var i StepRunResultArchive
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.DeletedAt,
|
||||
&i.StepRunId,
|
||||
&i.Order,
|
||||
&i.Input,
|
||||
&i.Output,
|
||||
&i.Error,
|
||||
&i.StartedAt,
|
||||
&i.FinishedAt,
|
||||
&i.TimeoutAt,
|
||||
&i.CancelledAt,
|
||||
&i.CancelledReason,
|
||||
&i.CancelledError,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
|
||||
const getStepRun = `-- name: GetStepRun :one
|
||||
SELECT
|
||||
"StepRun".id, "StepRun"."createdAt", "StepRun"."updatedAt", "StepRun"."deletedAt", "StepRun"."tenantId", "StepRun"."jobRunId", "StepRun"."stepId", "StepRun"."order", "StepRun"."workerId", "StepRun"."tickerId", "StepRun".status, "StepRun".input, "StepRun".output, "StepRun"."requeueAfter", "StepRun"."scheduleTimeoutAt", "StepRun".error, "StepRun"."startedAt", "StepRun"."finishedAt", "StepRun"."timeoutAt", "StepRun"."cancelledAt", "StepRun"."cancelledReason", "StepRun"."cancelledError", "StepRun"."inputSchema"
|
||||
"StepRun".id, "StepRun"."createdAt", "StepRun"."updatedAt", "StepRun"."deletedAt", "StepRun"."tenantId", "StepRun"."jobRunId", "StepRun"."stepId", "StepRun"."order", "StepRun"."workerId", "StepRun"."tickerId", "StepRun".status, "StepRun".input, "StepRun".output, "StepRun"."requeueAfter", "StepRun"."scheduleTimeoutAt", "StepRun".error, "StepRun"."startedAt", "StepRun"."finishedAt", "StepRun"."timeoutAt", "StepRun"."cancelledAt", "StepRun"."cancelledReason", "StepRun"."cancelledError", "StepRun"."inputSchema", "StepRun"."callerFiles", "StepRun"."gitRepoBranch"
|
||||
FROM
|
||||
"StepRun"
|
||||
WHERE
|
||||
@@ -53,13 +137,15 @@ func (q *Queries) GetStepRun(ctx context.Context, db DBTX, arg GetStepRunParams)
|
||||
&i.CancelledReason,
|
||||
&i.CancelledError,
|
||||
&i.InputSchema,
|
||||
&i.CallerFiles,
|
||||
&i.GitRepoBranch,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
|
||||
const resolveLaterStepRuns = `-- name: ResolveLaterStepRuns :many
|
||||
WITH currStepRun AS (
|
||||
SELECT id, "createdAt", "updatedAt", "deletedAt", "tenantId", "jobRunId", "stepId", "order", "workerId", "tickerId", status, input, output, "requeueAfter", "scheduleTimeoutAt", error, "startedAt", "finishedAt", "timeoutAt", "cancelledAt", "cancelledReason", "cancelledError", "inputSchema"
|
||||
SELECT id, "createdAt", "updatedAt", "deletedAt", "tenantId", "jobRunId", "stepId", "order", "workerId", "tickerId", status, input, output, "requeueAfter", "scheduleTimeoutAt", error, "startedAt", "finishedAt", "timeoutAt", "cancelledAt", "cancelledReason", "cancelledError", "inputSchema", "callerFiles", "gitRepoBranch"
|
||||
FROM "StepRun"
|
||||
WHERE
|
||||
"id" = $1::uuid AND
|
||||
@@ -92,7 +178,7 @@ WHERE
|
||||
WHERE "id" = $1::uuid
|
||||
) AND
|
||||
sr."tenantId" = $2::uuid
|
||||
RETURNING sr.id, sr."createdAt", sr."updatedAt", sr."deletedAt", sr."tenantId", sr."jobRunId", sr."stepId", sr."order", sr."workerId", sr."tickerId", sr.status, sr.input, sr.output, sr."requeueAfter", sr."scheduleTimeoutAt", sr.error, sr."startedAt", sr."finishedAt", sr."timeoutAt", sr."cancelledAt", sr."cancelledReason", sr."cancelledError", sr."inputSchema"
|
||||
RETURNING sr.id, sr."createdAt", sr."updatedAt", sr."deletedAt", sr."tenantId", sr."jobRunId", sr."stepId", sr."order", sr."workerId", sr."tickerId", sr.status, sr.input, sr.output, sr."requeueAfter", sr."scheduleTimeoutAt", sr.error, sr."startedAt", sr."finishedAt", sr."timeoutAt", sr."cancelledAt", sr."cancelledReason", sr."cancelledError", sr."inputSchema", sr."callerFiles", sr."gitRepoBranch"
|
||||
`
|
||||
|
||||
type ResolveLaterStepRunsParams struct {
|
||||
@@ -133,6 +219,8 @@ func (q *Queries) ResolveLaterStepRuns(ctx context.Context, db DBTX, arg Resolve
|
||||
&i.CancelledReason,
|
||||
&i.CancelledError,
|
||||
&i.InputSchema,
|
||||
&i.CallerFiles,
|
||||
&i.GitRepoBranch,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -187,7 +275,7 @@ SET
|
||||
WHERE
|
||||
"id" = $12::uuid AND
|
||||
"tenantId" = $13::uuid
|
||||
RETURNING "StepRun".id, "StepRun"."createdAt", "StepRun"."updatedAt", "StepRun"."deletedAt", "StepRun"."tenantId", "StepRun"."jobRunId", "StepRun"."stepId", "StepRun"."order", "StepRun"."workerId", "StepRun"."tickerId", "StepRun".status, "StepRun".input, "StepRun".output, "StepRun"."requeueAfter", "StepRun"."scheduleTimeoutAt", "StepRun".error, "StepRun"."startedAt", "StepRun"."finishedAt", "StepRun"."timeoutAt", "StepRun"."cancelledAt", "StepRun"."cancelledReason", "StepRun"."cancelledError", "StepRun"."inputSchema"
|
||||
RETURNING "StepRun".id, "StepRun"."createdAt", "StepRun"."updatedAt", "StepRun"."deletedAt", "StepRun"."tenantId", "StepRun"."jobRunId", "StepRun"."stepId", "StepRun"."order", "StepRun"."workerId", "StepRun"."tickerId", "StepRun".status, "StepRun".input, "StepRun".output, "StepRun"."requeueAfter", "StepRun"."scheduleTimeoutAt", "StepRun".error, "StepRun"."startedAt", "StepRun"."finishedAt", "StepRun"."timeoutAt", "StepRun"."cancelledAt", "StepRun"."cancelledReason", "StepRun"."cancelledError", "StepRun"."inputSchema", "StepRun"."callerFiles", "StepRun"."gitRepoBranch"
|
||||
`
|
||||
|
||||
type UpdateStepRunParams struct {
|
||||
@@ -247,6 +335,8 @@ func (q *Queries) UpdateStepRun(ctx context.Context, db DBTX, arg UpdateStepRunP
|
||||
&i.CancelledReason,
|
||||
&i.CancelledError,
|
||||
&i.InputSchema,
|
||||
&i.CallerFiles,
|
||||
&i.GitRepoBranch,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
@@ -278,27 +368,32 @@ func (q *Queries) UpdateStepRunInputSchema(ctx context.Context, db DBTX, arg Upd
|
||||
|
||||
const updateStepRunOverridesData = `-- name: UpdateStepRunOverridesData :one
|
||||
UPDATE
|
||||
"StepRun" as sr
|
||||
"StepRun" AS sr
|
||||
SET
|
||||
"updatedAt" = CURRENT_TIMESTAMP,
|
||||
"input" = jsonb_set("input", $1::text[], $2::jsonb, true)
|
||||
"input" = jsonb_set("input", $1::text[], $2::jsonb, true),
|
||||
"callerFiles" = jsonb_set("callerFiles", $3::text[], to_jsonb($4::text), true)
|
||||
WHERE
|
||||
sr."tenantId" = $3::uuid AND
|
||||
sr."id" = $4::uuid
|
||||
sr."tenantId" = $5::uuid AND
|
||||
sr."id" = $6::uuid
|
||||
RETURNING "input"
|
||||
`
|
||||
|
||||
type UpdateStepRunOverridesDataParams struct {
|
||||
Fieldpath []string `json:"fieldpath"`
|
||||
Jsondata []byte `json:"jsondata"`
|
||||
Tenantid pgtype.UUID `json:"tenantid"`
|
||||
Steprunid pgtype.UUID `json:"steprunid"`
|
||||
Fieldpath []string `json:"fieldpath"`
|
||||
Jsondata []byte `json:"jsondata"`
|
||||
Overrideskey []string `json:"overrideskey"`
|
||||
Callerfile string `json:"callerfile"`
|
||||
Tenantid pgtype.UUID `json:"tenantid"`
|
||||
Steprunid pgtype.UUID `json:"steprunid"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateStepRunOverridesData(ctx context.Context, db DBTX, arg UpdateStepRunOverridesDataParams) ([]byte, error) {
|
||||
row := db.QueryRow(ctx, updateStepRunOverridesData,
|
||||
arg.Fieldpath,
|
||||
arg.Jsondata,
|
||||
arg.Overrideskey,
|
||||
arg.Callerfile,
|
||||
arg.Tenantid,
|
||||
arg.Steprunid,
|
||||
)
|
||||
|
||||
@@ -341,7 +341,8 @@ INSERT INTO "StepRun" (
|
||||
"timeoutAt",
|
||||
"cancelledAt",
|
||||
"cancelledReason",
|
||||
"cancelledError"
|
||||
"cancelledError",
|
||||
"callerFiles"
|
||||
) VALUES (
|
||||
COALESCE(sqlc.narg('id')::uuid, gen_random_uuid()),
|
||||
CURRENT_TIMESTAMP,
|
||||
@@ -363,7 +364,8 @@ INSERT INTO "StepRun" (
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
NULL,
|
||||
'{}'
|
||||
) RETURNING *;
|
||||
|
||||
-- name: LinkStepRunParents :exec
|
||||
|
||||
@@ -305,7 +305,8 @@ INSERT INTO "StepRun" (
|
||||
"timeoutAt",
|
||||
"cancelledAt",
|
||||
"cancelledReason",
|
||||
"cancelledError"
|
||||
"cancelledError",
|
||||
"callerFiles"
|
||||
) VALUES (
|
||||
COALESCE($1::uuid, gen_random_uuid()),
|
||||
CURRENT_TIMESTAMP,
|
||||
@@ -327,8 +328,9 @@ INSERT INTO "StepRun" (
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
) RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "jobRunId", "stepId", "order", "workerId", "tickerId", status, input, output, "requeueAfter", "scheduleTimeoutAt", error, "startedAt", "finishedAt", "timeoutAt", "cancelledAt", "cancelledReason", "cancelledError", "inputSchema"
|
||||
NULL,
|
||||
'{}'
|
||||
) RETURNING id, "createdAt", "updatedAt", "deletedAt", "tenantId", "jobRunId", "stepId", "order", "workerId", "tickerId", status, input, output, "requeueAfter", "scheduleTimeoutAt", error, "startedAt", "finishedAt", "timeoutAt", "cancelledAt", "cancelledReason", "cancelledError", "inputSchema", "callerFiles", "gitRepoBranch"
|
||||
`
|
||||
|
||||
type CreateStepRunParams struct {
|
||||
@@ -374,6 +376,8 @@ func (q *Queries) CreateStepRun(ctx context.Context, db DBTX, arg CreateStepRunP
|
||||
&i.CancelledReason,
|
||||
&i.CancelledError,
|
||||
&i.InputSchema,
|
||||
&i.CallerFiles,
|
||||
&i.GitRepoBranch,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
@@ -403,7 +407,7 @@ INSERT INTO "WorkflowRun" (
|
||||
NULL, -- assuming error is not set on creation
|
||||
NULL, -- assuming startedAt is not set on creation
|
||||
NULL -- assuming finishedAt is not set on creation
|
||||
) RETURNING "createdAt", "updatedAt", "deletedAt", "tenantId", "workflowVersionId", status, error, "startedAt", "finishedAt", "concurrencyGroupId", "displayName", id
|
||||
) RETURNING "createdAt", "updatedAt", "deletedAt", "tenantId", "workflowVersionId", status, error, "startedAt", "finishedAt", "concurrencyGroupId", "displayName", id, "gitRepoBranch"
|
||||
`
|
||||
|
||||
type CreateWorkflowRunParams struct {
|
||||
@@ -434,6 +438,7 @@ func (q *Queries) CreateWorkflowRun(ctx context.Context, db DBTX, arg CreateWork
|
||||
&i.ConcurrencyGroupId,
|
||||
&i.DisplayName,
|
||||
&i.ID,
|
||||
&i.GitRepoBranch,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
@@ -519,7 +524,7 @@ func (q *Queries) LinkStepRunParents(ctx context.Context, db DBTX, jobrunid pgty
|
||||
|
||||
const listStartableStepRuns = `-- name: ListStartableStepRuns :many
|
||||
SELECT
|
||||
child_run.id, child_run."createdAt", child_run."updatedAt", child_run."deletedAt", child_run."tenantId", child_run."jobRunId", child_run."stepId", child_run."order", child_run."workerId", child_run."tickerId", child_run.status, child_run.input, child_run.output, child_run."requeueAfter", child_run."scheduleTimeoutAt", child_run.error, child_run."startedAt", child_run."finishedAt", child_run."timeoutAt", child_run."cancelledAt", child_run."cancelledReason", child_run."cancelledError", child_run."inputSchema"
|
||||
child_run.id, child_run."createdAt", child_run."updatedAt", child_run."deletedAt", child_run."tenantId", child_run."jobRunId", child_run."stepId", child_run."order", child_run."workerId", child_run."tickerId", child_run.status, child_run.input, child_run.output, child_run."requeueAfter", child_run."scheduleTimeoutAt", child_run.error, child_run."startedAt", child_run."finishedAt", child_run."timeoutAt", child_run."cancelledAt", child_run."cancelledReason", child_run."cancelledError", child_run."inputSchema", child_run."callerFiles", child_run."gitRepoBranch"
|
||||
FROM
|
||||
"StepRun" AS child_run
|
||||
JOIN
|
||||
@@ -578,6 +583,8 @@ func (q *Queries) ListStartableStepRuns(ctx context.Context, db DBTX, arg ListSt
|
||||
&i.CancelledReason,
|
||||
&i.CancelledError,
|
||||
&i.InputSchema,
|
||||
&i.CallerFiles,
|
||||
&i.GitRepoBranch,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -591,7 +598,7 @@ func (q *Queries) ListStartableStepRuns(ctx context.Context, db DBTX, arg ListSt
|
||||
|
||||
const listWorkflowRuns = `-- name: ListWorkflowRuns :many
|
||||
SELECT
|
||||
runs."createdAt", runs."updatedAt", runs."deletedAt", runs."tenantId", runs."workflowVersionId", runs.status, runs.error, runs."startedAt", runs."finishedAt", runs."concurrencyGroupId", runs."displayName", runs.id,
|
||||
runs."createdAt", runs."updatedAt", runs."deletedAt", runs."tenantId", runs."workflowVersionId", runs.status, runs.error, runs."startedAt", runs."finishedAt", runs."concurrencyGroupId", runs."displayName", runs.id, runs."gitRepoBranch",
|
||||
workflow.id, workflow."createdAt", workflow."updatedAt", workflow."deletedAt", workflow."tenantId", workflow.name, workflow.description,
|
||||
runtriggers.id, runtriggers."createdAt", runtriggers."updatedAt", runtriggers."deletedAt", runtriggers."tenantId", runtriggers."eventId", runtriggers."cronParentId", runtriggers."cronSchedule", runtriggers."scheduledId", runtriggers.input, runtriggers."parentId",
|
||||
workflowversion.id, workflowversion."createdAt", workflowversion."updatedAt", workflowversion."deletedAt", workflowversion.version, workflowversion."order", workflowversion."workflowId", workflowversion.checksum,
|
||||
@@ -693,6 +700,7 @@ func (q *Queries) ListWorkflowRuns(ctx context.Context, db DBTX, arg ListWorkflo
|
||||
&i.WorkflowRun.ConcurrencyGroupId,
|
||||
&i.WorkflowRun.DisplayName,
|
||||
&i.WorkflowRun.ID,
|
||||
&i.WorkflowRun.GitRepoBranch,
|
||||
&i.Workflow.ID,
|
||||
&i.Workflow.CreatedAt,
|
||||
&i.Workflow.UpdatedAt,
|
||||
@@ -783,7 +791,7 @@ WHERE "id" = (
|
||||
FROM "JobRun"
|
||||
WHERE "id" = $1::uuid
|
||||
) AND "tenantId" = $2::uuid
|
||||
RETURNING "WorkflowRun"."createdAt", "WorkflowRun"."updatedAt", "WorkflowRun"."deletedAt", "WorkflowRun"."tenantId", "WorkflowRun"."workflowVersionId", "WorkflowRun".status, "WorkflowRun".error, "WorkflowRun"."startedAt", "WorkflowRun"."finishedAt", "WorkflowRun"."concurrencyGroupId", "WorkflowRun"."displayName", "WorkflowRun".id
|
||||
RETURNING "WorkflowRun"."createdAt", "WorkflowRun"."updatedAt", "WorkflowRun"."deletedAt", "WorkflowRun"."tenantId", "WorkflowRun"."workflowVersionId", "WorkflowRun".status, "WorkflowRun".error, "WorkflowRun"."startedAt", "WorkflowRun"."finishedAt", "WorkflowRun"."concurrencyGroupId", "WorkflowRun"."displayName", "WorkflowRun".id, "WorkflowRun"."gitRepoBranch"
|
||||
`
|
||||
|
||||
type ResolveWorkflowRunStatusParams struct {
|
||||
@@ -807,6 +815,7 @@ func (q *Queries) ResolveWorkflowRunStatus(ctx context.Context, db DBTX, arg Res
|
||||
&i.ConcurrencyGroupId,
|
||||
&i.DisplayName,
|
||||
&i.ID,
|
||||
&i.GitRepoBranch,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
@@ -840,7 +849,7 @@ FROM
|
||||
WHERE
|
||||
workflowRun."id" = groupKeyRun."workflowRunId" AND
|
||||
workflowRun."tenantId" = $1::uuid
|
||||
RETURNING workflowrun."createdAt", workflowrun."updatedAt", workflowrun."deletedAt", workflowrun."tenantId", workflowrun."workflowVersionId", workflowrun.status, workflowrun.error, workflowrun."startedAt", workflowrun."finishedAt", workflowrun."concurrencyGroupId", workflowrun."displayName", workflowrun.id
|
||||
RETURNING workflowrun."createdAt", workflowrun."updatedAt", workflowrun."deletedAt", workflowrun."tenantId", workflowrun."workflowVersionId", workflowrun.status, workflowrun.error, workflowrun."startedAt", workflowrun."finishedAt", workflowrun."concurrencyGroupId", workflowrun."displayName", workflowrun.id, workflowrun."gitRepoBranch"
|
||||
`
|
||||
|
||||
type UpdateWorkflowRunGroupKeyParams struct {
|
||||
@@ -864,6 +873,7 @@ func (q *Queries) UpdateWorkflowRunGroupKey(ctx context.Context, db DBTX, arg Up
|
||||
&i.ConcurrencyGroupId,
|
||||
&i.DisplayName,
|
||||
&i.ID,
|
||||
&i.GitRepoBranch,
|
||||
)
|
||||
return &i, err
|
||||
}
|
||||
|
||||
@@ -612,7 +612,7 @@ func (q *Queries) ListWorkflows(ctx context.Context, db DBTX, arg ListWorkflowsP
|
||||
|
||||
const listWorkflowsLatestRuns = `-- name: ListWorkflowsLatestRuns :many
|
||||
SELECT
|
||||
DISTINCT ON (workflow."id") runs."createdAt", runs."updatedAt", runs."deletedAt", runs."tenantId", runs."workflowVersionId", runs.status, runs.error, runs."startedAt", runs."finishedAt", runs."concurrencyGroupId", runs."displayName", runs.id, workflow."id" as "workflowId"
|
||||
DISTINCT ON (workflow."id") runs."createdAt", runs."updatedAt", runs."deletedAt", runs."tenantId", runs."workflowVersionId", runs.status, runs.error, runs."startedAt", runs."finishedAt", runs."concurrencyGroupId", runs."displayName", runs.id, runs."gitRepoBranch", workflow."id" as "workflowId"
|
||||
FROM
|
||||
"WorkflowRun" as runs
|
||||
LEFT JOIN
|
||||
@@ -683,6 +683,7 @@ func (q *Queries) ListWorkflowsLatestRuns(ctx context.Context, db DBTX, arg List
|
||||
&i.WorkflowRun.ConcurrencyGroupId,
|
||||
&i.WorkflowRun.DisplayName,
|
||||
&i.WorkflowRun.ID,
|
||||
&i.WorkflowRun.GitRepoBranch,
|
||||
&i.WorkflowId,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
package prisma
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/internal/repository"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
"github.com/hatchet-dev/hatchet/internal/validator"
|
||||
)
|
||||
|
||||
type githubRepository struct {
|
||||
client *db.PrismaClient
|
||||
v validator.Validator
|
||||
}
|
||||
|
||||
func NewGithubRepository(client *db.PrismaClient, v validator.Validator) repository.GithubRepository {
|
||||
return &githubRepository{
|
||||
client: client,
|
||||
v: v,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *githubRepository) CreateInstallation(githubUserId int, opts *repository.CreateInstallationOpts) (*db.GithubAppInstallationModel, error) {
|
||||
if err := r.v.Validate(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.client.GithubAppInstallation.CreateOne(
|
||||
db.GithubAppInstallation.GithubAppOAuth.Link(
|
||||
db.GithubAppOAuth.GithubUserID.Equals(githubUserId),
|
||||
),
|
||||
db.GithubAppInstallation.InstallationID.Set(opts.InstallationID),
|
||||
db.GithubAppInstallation.AccountName.Set(opts.AccountName),
|
||||
db.GithubAppInstallation.AccountID.Set(opts.AccountID),
|
||||
db.GithubAppInstallation.Config.SetIfPresent(opts.Config),
|
||||
db.GithubAppInstallation.AccountAvatarURL.SetIfPresent(opts.AccountAvatarURL),
|
||||
db.GithubAppInstallation.InstallationSettingsURL.SetIfPresent(opts.InstallationSettingsURL),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) AddGithubUserIdToInstallation(installationID, accountID, githubUserId int) (*db.GithubAppInstallationModel, error) {
|
||||
return r.client.GithubAppInstallation.FindUnique(
|
||||
db.GithubAppInstallation.InstallationIDAccountID(
|
||||
db.GithubAppInstallation.InstallationID.Equals(installationID),
|
||||
db.GithubAppInstallation.AccountID.Equals(accountID),
|
||||
),
|
||||
).Update(
|
||||
db.GithubAppInstallation.GithubAppOAuth.Link(
|
||||
db.GithubAppOAuth.GithubUserID.Equals(githubUserId),
|
||||
),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) ReadGithubAppInstallationByID(installationId string) (*db.GithubAppInstallationModel, error) {
|
||||
return r.client.GithubAppInstallation.FindUnique(
|
||||
db.GithubAppInstallation.ID.Equals(installationId),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) ReadGithubWebhook(tenantId, repoOwner, repoName string) (*db.GithubWebhookModel, error) {
|
||||
return r.client.GithubWebhook.FindUnique(
|
||||
db.GithubWebhook.TenantIDRepositoryOwnerRepositoryName(
|
||||
db.GithubWebhook.TenantID.Equals(tenantId),
|
||||
db.GithubWebhook.RepositoryOwner.Equals(repoOwner),
|
||||
db.GithubWebhook.RepositoryName.Equals(repoName),
|
||||
),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) ReadGithubWebhookById(id string) (*db.GithubWebhookModel, error) {
|
||||
return r.client.GithubWebhook.FindUnique(
|
||||
db.GithubWebhook.ID.Equals(id),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) ReadGithubAppOAuthByGithubUserID(githubUserId int) (*db.GithubAppOAuthModel, error) {
|
||||
return r.client.GithubAppOAuth.FindUnique(
|
||||
db.GithubAppOAuth.GithubUserID.Equals(githubUserId),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) ReadGithubAppInstallationByInstallationAndAccountID(installationID, accountID int) (*db.GithubAppInstallationModel, error) {
|
||||
return r.client.GithubAppInstallation.FindUnique(
|
||||
db.GithubAppInstallation.InstallationIDAccountID(
|
||||
db.GithubAppInstallation.InstallationID.Equals(installationID),
|
||||
db.GithubAppInstallation.AccountID.Equals(accountID),
|
||||
),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) CreateGithubWebhook(tenantId string, opts *repository.CreateGithubWebhookOpts) (*db.GithubWebhookModel, error) {
|
||||
if err := r.v.Validate(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.client.GithubWebhook.CreateOne(
|
||||
db.GithubWebhook.Tenant.Link(
|
||||
db.Tenant.ID.Equals(tenantId),
|
||||
),
|
||||
db.GithubWebhook.RepositoryOwner.Set(opts.RepoOwner),
|
||||
db.GithubWebhook.RepositoryName.Set(opts.RepoName),
|
||||
db.GithubWebhook.SigningSecret.Set(opts.SigningSecret),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) UpsertGithubAppOAuth(userId string, opts *repository.CreateGithubAppOAuthOpts) (*db.GithubAppOAuthModel, error) {
|
||||
if err := r.v.Validate(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.client.GithubAppOAuth.UpsertOne(
|
||||
db.GithubAppOAuth.GithubUserID.Equals(opts.GithubUserID),
|
||||
).Create(
|
||||
db.GithubAppOAuth.GithubUserID.Set(opts.GithubUserID),
|
||||
db.GithubAppOAuth.AccessToken.Set(opts.AccessToken),
|
||||
db.GithubAppOAuth.RefreshToken.SetIfPresent(opts.RefreshToken),
|
||||
db.GithubAppOAuth.ExpiresAt.SetIfPresent(opts.ExpiresAt),
|
||||
db.GithubAppOAuth.Users.Link(
|
||||
db.User.ID.Equals(userId),
|
||||
),
|
||||
).Update(
|
||||
db.GithubAppOAuth.AccessToken.Set(opts.AccessToken),
|
||||
db.GithubAppOAuth.RefreshToken.SetIfPresent(opts.RefreshToken),
|
||||
db.GithubAppOAuth.ExpiresAt.SetIfPresent(opts.ExpiresAt),
|
||||
db.GithubAppOAuth.Users.Link(
|
||||
db.User.ID.Equals(userId),
|
||||
),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) CanUserAccessInstallation(installationId, userId string) (bool, error) {
|
||||
installation, err := r.client.GithubAppInstallation.FindFirst(
|
||||
db.GithubAppInstallation.ID.Equals(installationId),
|
||||
db.GithubAppInstallation.GithubAppOAuth.Where(
|
||||
db.GithubAppOAuth.Users.Some(
|
||||
db.User.ID.Equals(userId),
|
||||
),
|
||||
),
|
||||
).Exec(context.Background())
|
||||
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return installation != nil, nil
|
||||
}
|
||||
|
||||
func (r *githubRepository) DeleteInstallation(installationId, accountId int) (*db.GithubAppInstallationModel, error) {
|
||||
return r.client.GithubAppInstallation.FindUnique(
|
||||
db.GithubAppInstallation.InstallationIDAccountID(
|
||||
db.GithubAppInstallation.InstallationID.Equals(installationId),
|
||||
db.GithubAppInstallation.AccountID.Equals(accountId),
|
||||
),
|
||||
).Delete().Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) ListGithubAppInstallationsByUserID(userId string) ([]db.GithubAppInstallationModel, error) {
|
||||
return r.client.GithubAppInstallation.FindMany(
|
||||
db.GithubAppInstallation.GithubAppOAuth.Where(
|
||||
db.GithubAppOAuth.Users.Some(
|
||||
db.User.ID.Equals(userId),
|
||||
),
|
||||
),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) UpdatePullRequest(tenantId, prId string, opts *repository.UpdatePullRequestOpts) (*db.GithubPullRequestModel, error) {
|
||||
if err := r.v.Validate(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.client.GithubPullRequest.FindUnique(
|
||||
db.GithubPullRequest.ID.Equals(prId),
|
||||
).Update(
|
||||
db.GithubPullRequest.PullRequestState.SetIfPresent(opts.State),
|
||||
db.GithubPullRequest.PullRequestHeadBranch.SetIfPresent(opts.HeadBranch),
|
||||
db.GithubPullRequest.PullRequestBaseBranch.SetIfPresent(opts.BaseBranch),
|
||||
db.GithubPullRequest.PullRequestTitle.SetIfPresent(opts.Title),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *githubRepository) GetPullRequest(tenantId, repoOwner, repoName string, prNumber int) (*db.GithubPullRequestModel, error) {
|
||||
return r.client.GithubPullRequest.FindUnique(
|
||||
db.GithubPullRequest.TenantIDRepositoryOwnerRepositoryNamePullRequestNumber(
|
||||
db.GithubPullRequest.TenantID.Equals(tenantId),
|
||||
db.GithubPullRequest.RepositoryOwner.Equals(repoOwner),
|
||||
db.GithubPullRequest.RepositoryName.Equals(repoName),
|
||||
db.GithubPullRequest.PullRequestNumber.Equals(prNumber),
|
||||
),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
@@ -19,6 +19,7 @@ type prismaRepository struct {
|
||||
jobRun repository.JobRunRepository
|
||||
stepRun repository.StepRunRepository
|
||||
getGroupKeyRun repository.GetGroupKeyRunRepository
|
||||
github repository.GithubRepository
|
||||
step repository.StepRepository
|
||||
dispatcher repository.DispatcherRepository
|
||||
worker repository.WorkerRepository
|
||||
@@ -72,6 +73,7 @@ func NewPrismaRepository(client *db.PrismaClient, pool *pgxpool.Pool, fs ...Pris
|
||||
jobRun: NewJobRunRepository(client, pool, opts.v, opts.l),
|
||||
stepRun: NewStepRunRepository(client, pool, opts.v, opts.l),
|
||||
getGroupKeyRun: NewGetGroupKeyRunRepository(client, pool, opts.v, opts.l),
|
||||
github: NewGithubRepository(client, opts.v),
|
||||
step: NewStepRepository(client, opts.v),
|
||||
dispatcher: NewDispatcherRepository(client, pool, opts.v, opts.l),
|
||||
worker: NewWorkerRepository(client, opts.v),
|
||||
@@ -117,6 +119,10 @@ func (r *prismaRepository) GetGroupKeyRun() repository.GetGroupKeyRunRepository
|
||||
return r.getGroupKeyRun
|
||||
}
|
||||
|
||||
func (r *prismaRepository) Github() repository.GithubRepository {
|
||||
return r.github
|
||||
}
|
||||
|
||||
func (r *prismaRepository) Step() repository.StepRepository {
|
||||
return r.step
|
||||
}
|
||||
|
||||
@@ -245,6 +245,12 @@ func (s *stepRunRepository) UpdateStepRunOverridesData(tenantId, stepRunId strin
|
||||
pgTenantId := sqlchelpers.UUIDFromStr(tenantId)
|
||||
pgStepRunId := sqlchelpers.UUIDFromStr(stepRunId)
|
||||
|
||||
callerFile := ""
|
||||
|
||||
if opts.CallerFile != nil {
|
||||
callerFile = *opts.CallerFile
|
||||
}
|
||||
|
||||
input, err := s.queries.UpdateStepRunOverridesData(
|
||||
context.Background(),
|
||||
tx,
|
||||
@@ -256,6 +262,10 @@ func (s *stepRunRepository) UpdateStepRunOverridesData(tenantId, stepRunId strin
|
||||
opts.OverrideKey,
|
||||
},
|
||||
Jsondata: opts.Data,
|
||||
Overrideskey: []string{
|
||||
opts.OverrideKey,
|
||||
},
|
||||
Callerfile: callerFile,
|
||||
},
|
||||
)
|
||||
|
||||
@@ -581,3 +591,52 @@ func (s *stepRunRepository) ListStartableStepRuns(tenantId, jobRunId, parentStep
|
||||
|
||||
return stepRuns, nil
|
||||
}
|
||||
|
||||
func (s *stepRunRepository) ArchiveStepRunResult(tenantId, stepRunId string) error {
|
||||
tx, err := s.pool.Begin(context.Background())
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer deferRollback(context.Background(), s.l, tx.Rollback)
|
||||
|
||||
_, err = s.queries.ArchiveStepRunResultFromStepRun(context.Background(), tx, dbsqlc.ArchiveStepRunResultFromStepRunParams{
|
||||
Tenantid: sqlchelpers.UUIDFromStr(tenantId),
|
||||
Steprunid: sqlchelpers.UUIDFromStr(stepRunId),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = tx.Commit(context.Background())
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *stepRunRepository) ListArchivedStepRunResults(tenantId, stepRunId string) ([]db.StepRunResultArchiveModel, error) {
|
||||
return s.client.StepRunResultArchive.FindMany(
|
||||
db.StepRunResultArchive.StepRunID.Equals(stepRunId),
|
||||
db.StepRunResultArchive.StepRun.Where(
|
||||
db.StepRun.TenantID.Equals(tenantId),
|
||||
),
|
||||
).OrderBy(
|
||||
db.StepRunResultArchive.Order.Order(db.DESC),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (s *stepRunRepository) GetFirstArchivedStepRunResult(tenantId, stepRunId string) (*db.StepRunResultArchiveModel, error) {
|
||||
return s.client.StepRunResultArchive.FindFirst(
|
||||
db.StepRunResultArchive.StepRunID.Equals(stepRunId),
|
||||
db.StepRunResultArchive.StepRun.Where(
|
||||
db.StepRun.TenantID.Equals(tenantId),
|
||||
),
|
||||
).OrderBy(
|
||||
db.StepRunResultArchive.Order.Order(db.ASC),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
@@ -704,9 +704,44 @@ func (r *workflowRepository) GetWorkflowVersionById(tenantId, workflowVersionId
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (r *workflowRepository) UpsertWorkflowDeploymentConfig(workflowId string, opts *repository.UpsertWorkflowDeploymentConfigOpts) (*db.WorkflowDeploymentConfigModel, error) {
|
||||
if err := r.v.Validate(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// upsert the deployment config
|
||||
deploymentConfig, err := r.client.WorkflowDeploymentConfig.UpsertOne(
|
||||
db.WorkflowDeploymentConfig.WorkflowID.Equals(workflowId),
|
||||
).Create(
|
||||
db.WorkflowDeploymentConfig.Workflow.Link(
|
||||
db.Workflow.ID.Equals(workflowId),
|
||||
),
|
||||
db.WorkflowDeploymentConfig.GitRepoName.Set(opts.GitRepoName),
|
||||
db.WorkflowDeploymentConfig.GitRepoOwner.Set(opts.GitRepoOwner),
|
||||
db.WorkflowDeploymentConfig.GitRepoBranch.Set(opts.GitRepoBranch),
|
||||
db.WorkflowDeploymentConfig.GithubAppInstallation.Link(
|
||||
db.GithubAppInstallation.ID.Equals(opts.GithubAppInstallationId),
|
||||
),
|
||||
).Update(
|
||||
db.WorkflowDeploymentConfig.GitRepoName.Set(opts.GitRepoName),
|
||||
db.WorkflowDeploymentConfig.GitRepoOwner.Set(opts.GitRepoOwner),
|
||||
db.WorkflowDeploymentConfig.GitRepoBranch.Set(opts.GitRepoBranch),
|
||||
db.WorkflowDeploymentConfig.GithubAppInstallation.Link(
|
||||
db.GithubAppInstallation.ID.Equals(opts.GithubAppInstallationId),
|
||||
),
|
||||
).Exec(context.Background())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return deploymentConfig, nil
|
||||
}
|
||||
|
||||
func defaultWorkflowPopulator() []db.WorkflowRelationWith {
|
||||
return []db.WorkflowRelationWith{
|
||||
db.Workflow.Tags.Fetch(),
|
||||
db.Workflow.DeploymentConfig.Fetch(),
|
||||
db.Workflow.Versions.Fetch().OrderBy(
|
||||
db.WorkflowVersion.Order.Order(db.SortOrderDesc),
|
||||
).With(
|
||||
|
||||
@@ -370,6 +370,46 @@ func (w *workflowRunRepository) GetWorkflowRunById(tenantId, id string) (*db.Wor
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (s *workflowRunRepository) CreateWorkflowRunPullRequest(tenantId, workflowRunId string, opts *repository.CreateWorkflowRunPullRequestOpts) (*db.GithubPullRequestModel, error) {
|
||||
return s.client.GithubPullRequest.CreateOne(
|
||||
db.GithubPullRequest.Tenant.Link(
|
||||
db.Tenant.ID.Equals(tenantId),
|
||||
),
|
||||
db.GithubPullRequest.RepositoryOwner.Set(opts.RepositoryOwner),
|
||||
db.GithubPullRequest.RepositoryName.Set(opts.RepositoryName),
|
||||
db.GithubPullRequest.PullRequestID.Set(opts.PullRequestID),
|
||||
db.GithubPullRequest.PullRequestTitle.Set(opts.PullRequestTitle),
|
||||
db.GithubPullRequest.PullRequestNumber.Set(opts.PullRequestNumber),
|
||||
db.GithubPullRequest.PullRequestHeadBranch.Set(opts.PullRequestHeadBranch),
|
||||
db.GithubPullRequest.PullRequestBaseBranch.Set(opts.PullRequestBaseBranch),
|
||||
db.GithubPullRequest.PullRequestState.Set(opts.PullRequestState),
|
||||
db.GithubPullRequest.WorkflowRuns.Link(
|
||||
db.WorkflowRun.ID.Equals(workflowRunId),
|
||||
),
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func (s *workflowRunRepository) ListPullRequestsForWorkflowRun(tenantId, workflowRunId string, opts *repository.ListPullRequestsForWorkflowRunOpts) ([]db.GithubPullRequestModel, error) {
|
||||
if err := s.v.Validate(opts); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
optionals := []db.GithubPullRequestWhereParam{
|
||||
db.GithubPullRequest.WorkflowRuns.Some(
|
||||
db.WorkflowRun.ID.Equals(workflowRunId),
|
||||
db.WorkflowRun.TenantID.Equals(tenantId),
|
||||
),
|
||||
}
|
||||
|
||||
if opts.State != nil {
|
||||
optionals = append(optionals, db.GithubPullRequest.PullRequestState.Equals(*opts.State))
|
||||
}
|
||||
|
||||
return s.client.GithubPullRequest.FindMany(
|
||||
optionals...,
|
||||
).Exec(context.Background())
|
||||
}
|
||||
|
||||
func defaultWorkflowRunPopulator() []db.WorkflowRunRelationWith {
|
||||
return []db.WorkflowRunRelationWith{
|
||||
db.WorkflowRun.WorkflowVersion.Fetch().With(
|
||||
|
||||
@@ -10,6 +10,7 @@ type Repository interface {
|
||||
JobRun() JobRunRepository
|
||||
StepRun() StepRunRepository
|
||||
GetGroupKeyRun() GetGroupKeyRunRepository
|
||||
Github() GithubRepository
|
||||
Step() StepRepository
|
||||
Dispatcher() DispatcherRepository
|
||||
Ticker() TickerRepository
|
||||
|
||||
@@ -55,6 +55,7 @@ type UpdateStepRunOpts struct {
|
||||
type UpdateStepRunOverridesDataOpts struct {
|
||||
OverrideKey string
|
||||
Data []byte
|
||||
CallerFile *string
|
||||
}
|
||||
|
||||
func StepRunStatusPtr(status db.StepRunStatus) *db.StepRunStatus {
|
||||
@@ -94,4 +95,10 @@ type StepRunRepository interface {
|
||||
CancelPendingStepRuns(tenantId, jobRunId, reason string) error
|
||||
|
||||
ListStartableStepRuns(tenantId, jobRunId, parentStepRunId string) ([]*dbsqlc.StepRun, error)
|
||||
|
||||
ArchiveStepRunResult(tenantId, stepRunId string) error
|
||||
|
||||
ListArchivedStepRunResults(tenantId, stepRunId string) ([]db.StepRunResultArchiveModel, error)
|
||||
|
||||
GetFirstArchivedStepRunResult(tenantId, stepRunId string) (*db.StepRunResultArchiveModel, error)
|
||||
}
|
||||
|
||||
@@ -141,6 +141,20 @@ func (e *JobRunHasCycleError) Error() string {
|
||||
return fmt.Sprintf("job %s has a cycle", e.JobName)
|
||||
}
|
||||
|
||||
type UpsertWorkflowDeploymentConfigOpts struct {
|
||||
// (required) the github app installation id
|
||||
GithubAppInstallationId string `validate:"required,uuid"`
|
||||
|
||||
// (required) the github repository name
|
||||
GitRepoName string `validate:"required"`
|
||||
|
||||
// (required) the github repository owner
|
||||
GitRepoOwner string `validate:"required"`
|
||||
|
||||
// (required) the github repository branch
|
||||
GitRepoBranch string `validate:"required"`
|
||||
}
|
||||
|
||||
type WorkflowRepository interface {
|
||||
// ListWorkflows returns all workflows for a given tenant.
|
||||
ListWorkflows(tenantId string, opts *ListWorkflowsOpts) (*ListWorkflowsResult, error)
|
||||
@@ -175,4 +189,6 @@ type WorkflowRepository interface {
|
||||
|
||||
// DeleteWorkflow deletes a workflow for a given tenant.
|
||||
DeleteWorkflow(tenantId, workflowId string) (*db.WorkflowModel, error)
|
||||
|
||||
UpsertWorkflowDeploymentConfig(workflowId string, opts *UpsertWorkflowDeploymentConfigOpts) (*db.WorkflowDeploymentConfigModel, error)
|
||||
}
|
||||
|
||||
@@ -219,6 +219,21 @@ type ListWorkflowRunsResult struct {
|
||||
Count int
|
||||
}
|
||||
|
||||
type CreateWorkflowRunPullRequestOpts struct {
|
||||
RepositoryOwner string
|
||||
RepositoryName string
|
||||
PullRequestID int
|
||||
PullRequestTitle string
|
||||
PullRequestNumber int
|
||||
PullRequestHeadBranch string
|
||||
PullRequestBaseBranch string
|
||||
PullRequestState string
|
||||
}
|
||||
|
||||
type ListPullRequestsForWorkflowRunOpts struct {
|
||||
State *string
|
||||
}
|
||||
|
||||
type WorkflowRunRepository interface {
|
||||
// ListWorkflowRuns returns workflow runs for a given workflow version id.
|
||||
ListWorkflowRuns(tenantId string, opts *ListWorkflowRunsOpts) (*ListWorkflowRunsResult, error)
|
||||
@@ -228,4 +243,8 @@ type WorkflowRunRepository interface {
|
||||
|
||||
// GetWorkflowRunById returns a workflow run by id.
|
||||
GetWorkflowRunById(tenantId, runId string) (*db.WorkflowRunModel, error)
|
||||
|
||||
CreateWorkflowRunPullRequest(tenantId, workflowRunId string, opts *CreateWorkflowRunPullRequestOpts) (*db.GithubPullRequestModel, error)
|
||||
|
||||
ListPullRequestsForWorkflowRun(tenantId, workflowRunId string, opts *ListPullRequestsForWorkflowRunOpts) ([]db.GithubPullRequestModel, error)
|
||||
}
|
||||
|
||||
@@ -1120,6 +1120,12 @@ func (x *WorkflowEvent) GetEventPayload() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *WorkflowEvent) GetHangup() bool {
|
||||
if x != nil {
|
||||
return x.Hangup
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type OverridesData struct {
|
||||
state protoimpl.MessageState
|
||||
@@ -1132,6 +1138,8 @@ type OverridesData struct {
|
||||
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
|
||||
// the value to set
|
||||
Value string `protobuf:"bytes,3,opt,name=value,proto3" json:"value,omitempty"`
|
||||
// the filename of the caller
|
||||
CallerFilename string `protobuf:"bytes,4,opt,name=callerFilename,proto3" json:"callerFilename,omitempty"`
|
||||
}
|
||||
|
||||
func (x *OverridesData) Reset() {
|
||||
@@ -1187,6 +1195,13 @@ func (x *OverridesData) GetValue() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *OverridesData) GetCallerFilename() string {
|
||||
if x != nil {
|
||||
return x.CallerFilename
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type OverridesDataResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
@@ -1349,12 +1364,16 @@ var file_dispatcher_proto_rawDesc = []byte{
|
||||
0x6d, 0x70, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61,
|
||||
0x6d, 0x70, 0x12, 0x22, 0x0a, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x79, 0x6c, 0x6f,
|
||||
0x61, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x50,
|
||||
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x22, 0x57, 0x0a, 0x0d, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69,
|
||||
0x64, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x74, 0x65, 0x70, 0x52,
|
||||
0x75, 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x65, 0x70,
|
||||
0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20,
|
||||
0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22,
|
||||
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x61, 0x6e, 0x67, 0x75, 0x70,
|
||||
0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x68, 0x61, 0x6e, 0x67, 0x75, 0x70, 0x22, 0x7f,
|
||||
0x0a, 0x0d, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61, 0x12,
|
||||
0x1c, 0x0a, 0x09, 0x73, 0x74, 0x65, 0x70, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x09, 0x73, 0x74, 0x65, 0x70, 0x52, 0x75, 0x6e, 0x49, 0x64, 0x12, 0x12, 0x0a,
|
||||
0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74,
|
||||
0x68, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
|
||||
0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x63, 0x61, 0x6c, 0x6c, 0x65,
|
||||
0x72, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||
0x0e, 0x63, 0x61, 0x6c, 0x6c, 0x65, 0x72, 0x46, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x22,
|
||||
0x17, 0x0a, 0x15, 0x4f, 0x76, 0x65, 0x72, 0x72, 0x69, 0x64, 0x65, 0x73, 0x44, 0x61, 0x74, 0x61,
|
||||
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2a, 0x4e, 0x0a, 0x0a, 0x41, 0x63, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x54, 0x41, 0x52, 0x54, 0x5f,
|
||||
|
||||
@@ -365,10 +365,16 @@ func (s *DispatcherImpl) PutOverridesData(ctx context.Context, request *contract
|
||||
return nil, fmt.Errorf("step run id is required")
|
||||
}
|
||||
|
||||
input, err := s.repo.StepRun().UpdateStepRunOverridesData(tenant.ID, request.StepRunId, &repository.UpdateStepRunOverridesDataOpts{
|
||||
opts := &repository.UpdateStepRunOverridesDataOpts{
|
||||
OverrideKey: request.Path,
|
||||
Data: []byte(request.Value),
|
||||
})
|
||||
}
|
||||
|
||||
if request.CallerFilename != "" {
|
||||
opts.CallerFile = &request.CallerFilename
|
||||
}
|
||||
|
||||
input, err := s.repo.StepRun().UpdateStepRunOverridesData(tenant.ID, request.StepRunId, opts)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,457 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/steebchen/prisma-client-go/runtime/types"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/internal/datautils"
|
||||
"github.com/hatchet-dev/hatchet/internal/encryption"
|
||||
"github.com/hatchet-dev/hatchet/internal/integrations/vcs"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository"
|
||||
"github.com/hatchet-dev/hatchet/internal/repository/prisma/db"
|
||||
"github.com/hatchet-dev/hatchet/pkg/client"
|
||||
"github.com/hatchet-dev/hatchet/pkg/worker"
|
||||
)
|
||||
|
||||
const (
|
||||
PullRequestWorkflow string = "create-pull-request"
|
||||
StartPullRequest string = "pull_request:start"
|
||||
)
|
||||
|
||||
type StartPullRequestEvent struct {
|
||||
TenantID string `json:"tenant_id"`
|
||||
StepRunID string `json:"step_run_id"`
|
||||
BranchName string `json:"branch_name"`
|
||||
}
|
||||
|
||||
type WorkerOpt func(*WorkerOpts)
|
||||
|
||||
type WorkerOpts struct {
|
||||
client client.Client
|
||||
|
||||
repo repository.Repository
|
||||
vcsProviders map[vcs.VCSRepositoryKind]vcs.VCSProvider
|
||||
}
|
||||
|
||||
func defaultWorkerOpts() *WorkerOpts {
|
||||
return &WorkerOpts{}
|
||||
}
|
||||
|
||||
func WithRepository(r repository.Repository) WorkerOpt {
|
||||
return func(opts *WorkerOpts) {
|
||||
opts.repo = r
|
||||
}
|
||||
}
|
||||
|
||||
func WithVCSProviders(vcsProviders map[vcs.VCSRepositoryKind]vcs.VCSProvider) WorkerOpt {
|
||||
return func(opts *WorkerOpts) {
|
||||
opts.vcsProviders = vcsProviders
|
||||
}
|
||||
}
|
||||
|
||||
func WithClient(c client.Client) WorkerOpt {
|
||||
return func(opts *WorkerOpts) {
|
||||
opts.client = c
|
||||
}
|
||||
}
|
||||
|
||||
type WorkerImpl struct {
|
||||
*worker.Worker
|
||||
|
||||
repo repository.Repository
|
||||
vcsProviders map[vcs.VCSRepositoryKind]vcs.VCSProvider
|
||||
}
|
||||
|
||||
func NewWorker(fs ...WorkerOpt) (*WorkerImpl, error) {
|
||||
opts := defaultWorkerOpts()
|
||||
|
||||
for _, f := range fs {
|
||||
f(opts)
|
||||
}
|
||||
|
||||
if opts.repo == nil {
|
||||
return nil, fmt.Errorf("repository is required. use WithRepository")
|
||||
}
|
||||
|
||||
if opts.client == nil {
|
||||
return nil, fmt.Errorf("client is required. use WithClient")
|
||||
}
|
||||
|
||||
hatchetWorker, err := worker.NewWorker(
|
||||
worker.WithClient(opts.client),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create worker: %w", err)
|
||||
}
|
||||
|
||||
return &WorkerImpl{
|
||||
Worker: hatchetWorker,
|
||||
repo: opts.repo,
|
||||
vcsProviders: opts.vcsProviders,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (w *WorkerImpl) Start(ctx context.Context) error {
|
||||
err := w.On(
|
||||
worker.Event(StartPullRequest),
|
||||
&worker.WorkflowJob{
|
||||
Name: PullRequestWorkflow,
|
||||
Description: "Workflow that creates a new pull request.",
|
||||
Timeout: "60s",
|
||||
Steps: []*worker.WorkflowStep{
|
||||
worker.Fn(w.handleStartPullRequest).SetName("start-pull-request"),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not register workflow: %w", err)
|
||||
}
|
||||
|
||||
// start the worker
|
||||
return w.Worker.Start(ctx)
|
||||
}
|
||||
|
||||
func (w *WorkerImpl) handleStartPullRequest(ctx worker.HatchetContext) error {
|
||||
var event StartPullRequestEvent
|
||||
|
||||
err := ctx.WorkflowInput(&event)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stepRun, err := w.repo.StepRun().GetStepRunById(event.TenantID, event.StepRunID)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get step run: %w", err)
|
||||
}
|
||||
|
||||
workflowRun, err := w.repo.WorkflowRun().GetWorkflowRunById(event.TenantID, stepRun.JobRun().WorkflowRunID)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get workflow run: %w", err)
|
||||
}
|
||||
|
||||
// read workflow
|
||||
workflow, err := w.repo.Workflow().GetWorkflowById(workflowRun.WorkflowVersion().WorkflowID)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get workflow: %w", err)
|
||||
}
|
||||
|
||||
if deploymentConfig, ok := workflow.DeploymentConfig(); ok {
|
||||
if installationId, ok := deploymentConfig.GithubAppInstallationID(); ok && installationId != "" {
|
||||
git, err := vcs.GetVCSRepositoryFromWorkflow(w.vcsProviders, workflow)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get VCS repository from workflow: %w", err)
|
||||
}
|
||||
|
||||
callerFilepaths := map[string]string{}
|
||||
|
||||
callerFilepathsJSON, _ := stepRun.CallerFiles()
|
||||
|
||||
err = json.Unmarshal(callerFilepathsJSON, &callerFilepaths)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not unmarshal caller filepaths: %w", err)
|
||||
}
|
||||
|
||||
callerFilepathsToRepoFiles := sync.Map{}
|
||||
|
||||
var errGroup errgroup.Group
|
||||
|
||||
for _, file := range callerFilepaths {
|
||||
fileCp := file
|
||||
|
||||
errGroup.Go(func() error {
|
||||
gitBranch, _ := stepRun.GitRepoBranch()
|
||||
|
||||
foundSearchPath, fileReader, err := searchForFile(fileCp, git, gitBranch)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not search for file: %w", err)
|
||||
}
|
||||
|
||||
if fileReader != nil {
|
||||
fileBytes, err := io.ReadAll(fileReader)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not read file: %w", err)
|
||||
}
|
||||
|
||||
callerFilepathsToRepoFiles.Store(foundSearchPath, fileBytes)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
if err = errGroup.Wait(); err != nil {
|
||||
return fmt.Errorf("could not search for files: %w", err)
|
||||
}
|
||||
|
||||
diffs, err := w.getStepRunOverrideDiffs(stepRun)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get step run override diffs: %w", err)
|
||||
}
|
||||
|
||||
newFiles := make(map[string][]byte)
|
||||
|
||||
callerFilepathsToRepoFiles.Range(func(key, value interface{}) bool {
|
||||
fileBytes := value.([]byte)
|
||||
|
||||
for diffKey, diffValue := range diffs {
|
||||
fileBytes, err = findAndReplace(fileBytes, diffKey, diffValue)
|
||||
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
newFiles[key.(string)] = fileBytes
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
prSuffix, err := encryption.GenerateRandomBytes(4)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not generate random bytes: %w", err)
|
||||
}
|
||||
|
||||
var baseBranch *string
|
||||
|
||||
if event.BranchName != "" {
|
||||
baseBranch = &event.BranchName
|
||||
}
|
||||
|
||||
// create the pull request
|
||||
_, err = git.CreateOrUpdatePullRequest(stepRun.TenantID, workflowRun.ID, &vcs.CreatePullRequestOpts{
|
||||
GitRepoOwner: git.GetRepoOwner(),
|
||||
GitRepoName: git.GetRepoName(),
|
||||
BaseBranch: baseBranch,
|
||||
Files: newFiles,
|
||||
Title: fmt.Sprintf("[Hatchet] Update %s", workflow.Name),
|
||||
HeadBranchName: fmt.Sprintf("hatchet/pr-%s", prSuffix),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create pull request: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *WorkerImpl) getStepRunOverrideDiffs(stepRun *db.StepRunModel) (map[string]string, error) {
|
||||
// get the first step run archived result, there will be at least one
|
||||
archivedResult, err := w.repo.StepRun().GetFirstArchivedStepRunResult(stepRun.TenantID, stepRun.ID)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get first archived step run result: %w", err)
|
||||
}
|
||||
|
||||
firstInput, err := getStepRunInput(archivedResult)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get input from archived result: %w", err)
|
||||
}
|
||||
|
||||
secondInput, err := getStepRunInput(stepRun)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get input from step run: %w", err)
|
||||
}
|
||||
|
||||
// compare the data
|
||||
diff := map[string]string{}
|
||||
|
||||
for key, value := range firstInput.Overrides {
|
||||
if secondValue, ok := secondInput.Overrides[key]; ok {
|
||||
if value != secondValue {
|
||||
newValue := formatNewValue(secondValue)
|
||||
|
||||
if newValue != "" {
|
||||
diff[key] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diff, nil
|
||||
}
|
||||
|
||||
func formatNewValue(val interface{}) string {
|
||||
switch v := val.(type) {
|
||||
case string:
|
||||
return strconv.Quote(v)
|
||||
case float64, bool:
|
||||
return fmt.Sprintf("%v", v)
|
||||
case nil:
|
||||
return "null"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type inputtable interface {
|
||||
Input() (value types.JSON, ok bool)
|
||||
}
|
||||
|
||||
func getStepRunInput(in inputtable) (*datautils.StepRunData, error) {
|
||||
input, ok := in.Input()
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("could not get input from inputtable")
|
||||
}
|
||||
|
||||
data := &datautils.StepRunData{}
|
||||
|
||||
if err := json.Unmarshal(input, data); err != nil || data == nil {
|
||||
return nil, fmt.Errorf("could not unmarshal input: %w", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func searchForFile(targetPath string, vcs vcs.VCSRepository, ref string) (string, io.ReadCloser, error) {
|
||||
if !filepath.IsAbs(targetPath) {
|
||||
return "", nil, fmt.Errorf("filepath must be absolute")
|
||||
}
|
||||
|
||||
searchPaths := getSearchPaths(targetPath)
|
||||
|
||||
var res io.ReadCloser
|
||||
var foundSearchPath string
|
||||
|
||||
for _, searchPath := range searchPaths {
|
||||
searchPathCp := searchPath
|
||||
file, err := vcs.ReadFile(ref, searchPathCp)
|
||||
|
||||
if err == nil {
|
||||
foundSearchPath = searchPathCp
|
||||
res = file
|
||||
}
|
||||
}
|
||||
|
||||
return foundSearchPath, res, nil
|
||||
}
|
||||
|
||||
// getSearchPaths returns paths from the most specific path to the least specific in the path. For
|
||||
// example, if the caller file is under `/usr/local/hatchet/src/worker.py`, and the file is under ./src/worker.py
|
||||
// in the repository, it will search in this order:
|
||||
// - `/usr/local/hatchet/src/worker.py`
|
||||
// - `/local/hatchet/src/worker.py`
|
||||
// - `/hatchet/src/worker.py`
|
||||
// - `/src/worker.py`
|
||||
func getSearchPaths(targetPath string) []string {
|
||||
base := filepath.Base(targetPath)
|
||||
searchBases := []string{}
|
||||
|
||||
if base != "" && base != "/" && base != "." && base != ".." {
|
||||
searchBases = append(searchBases, base)
|
||||
}
|
||||
|
||||
currDir := targetPath
|
||||
|
||||
for {
|
||||
currDir = filepath.Dir(currDir)
|
||||
|
||||
base := filepath.Base(currDir)
|
||||
|
||||
if base != "" && base != "/" && base != "." && base != ".." {
|
||||
searchBases = append([]string{base}, searchBases...)
|
||||
}
|
||||
|
||||
if currDir == "." || currDir == "/" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
searchPaths := []string{}
|
||||
|
||||
// construct search bases in reverse order
|
||||
for i := range searchBases {
|
||||
searchPath := strings.Join(searchBases[i:], "/")
|
||||
|
||||
if searchPath != "" {
|
||||
searchPaths = append(searchPaths, searchPath)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return searchPaths
|
||||
}
|
||||
|
||||
func searchWithRegex(input []byte, pattern string, replacements map[string]string) (string, error) {
|
||||
// Compile the regular expression with named capturing groups
|
||||
re, err := regexp.Compile(pattern)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Convert input to string for easier manipulation and because we work with indexes
|
||||
inputStr := string(input)
|
||||
offset := 0 // Keep track of the offset caused by replacements
|
||||
|
||||
// Find all matches
|
||||
matches := re.FindAllStringSubmatchIndex(inputStr, -1)
|
||||
for _, match := range matches {
|
||||
// Process each named group for replacement
|
||||
for i := 2; i < len(match); i += 2 {
|
||||
// Adjust group indexes by offset
|
||||
groupStart, groupEnd := match[i]+offset, match[i+1]+offset
|
||||
if groupStart == -1 || groupEnd == -1 {
|
||||
continue // Skip unmatched groups
|
||||
}
|
||||
|
||||
name := re.SubexpNames()[i/2] // Get the name of the current capturing group
|
||||
if replacement, ok := replacements[name]; ok && name != "" {
|
||||
// Perform the replacement in the input string
|
||||
before := inputStr[:groupStart]
|
||||
after := inputStr[groupEnd:]
|
||||
inputStr = before + replacement + after
|
||||
|
||||
// Update the offset
|
||||
offset += len(replacement) - (groupEnd - groupStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inputStr, nil
|
||||
}
|
||||
|
||||
func getPatternForValue(value string) string {
|
||||
return `(?P<instance>\w+)\.override\(\s*(?P<param1>('|"|""")\s*` + value + `\s*('|"|"""))\s*,\s*(?P<override>[\s\S]*?)\s*\)`
|
||||
}
|
||||
|
||||
func findAndReplace(input []byte, value, override string) ([]byte, error) {
|
||||
pattern := getPatternForValue(value)
|
||||
|
||||
replacements := map[string]string{
|
||||
"override": override,
|
||||
}
|
||||
|
||||
modifiedInput, err := searchWithRegex(input, pattern, replacements)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []byte(modifiedInput), nil
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetSearchPaths(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
targetPath string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "Basic Path",
|
||||
targetPath: "/usr/local/hatchet/src/worker.py",
|
||||
want: []string{
|
||||
"usr/local/hatchet/src/worker.py",
|
||||
"local/hatchet/src/worker.py",
|
||||
"hatchet/src/worker.py",
|
||||
"src/worker.py",
|
||||
"worker.py",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Root Path",
|
||||
targetPath: "/worker.py",
|
||||
want: []string{"worker.py"},
|
||||
},
|
||||
{
|
||||
name: "Empty Path",
|
||||
targetPath: "",
|
||||
want: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := getSearchPaths(tt.targetPath); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("[%s] getSearchPaths() = %v, want %v", tt.name, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindAndReplace(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
value string
|
||||
override string
|
||||
expected string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "Basic Replacement",
|
||||
input: `context.override("test", "Original override")`,
|
||||
value: "test",
|
||||
override: `"New override"`,
|
||||
expected: `context.override("test", "New override")`,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Multiple Replacement",
|
||||
input: `context.override("test", "Original override"); context.override("test", "Another original")`,
|
||||
value: "test",
|
||||
override: `"New override"`,
|
||||
expected: `context.override("test", "New override"); context.override("test", "New override")`,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Replace number",
|
||||
input: `context.override("test", 1234)`,
|
||||
value: "test",
|
||||
override: "5678",
|
||||
expected: `context.override("test", 5678)`,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "No Match",
|
||||
input: `context.override("no_match", "Original override")`,
|
||||
value: "test",
|
||||
override: `"New override"`,
|
||||
expected: `context.override("no_match", "Original override")`,
|
||||
expectError: false,
|
||||
},
|
||||
{
|
||||
name: "Error Handling - Invalid Regex",
|
||||
input: `context.override("test", "Original override")`,
|
||||
value: "(", // This makes the regex invalid
|
||||
override: `"New override"`,
|
||||
expected: "",
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := findAndReplace([]byte(tt.input), tt.value, tt.override)
|
||||
if (err != nil) != tt.expectError {
|
||||
t.Errorf("[%s] findAndReplace() error = %v, expectError %v", tt.name, err, tt.expectError)
|
||||
return
|
||||
}
|
||||
if string(result) != tt.expected {
|
||||
t.Errorf("[%s] findAndReplace() got = %v, want %v", tt.name, string(result), tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+40
-6
@@ -8,6 +8,7 @@ import (
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
|
||||
"github.com/hatchet-dev/hatchet/internal/config/client"
|
||||
"github.com/hatchet-dev/hatchet/internal/logger"
|
||||
"github.com/hatchet-dev/hatchet/internal/validator"
|
||||
"github.com/hatchet-dev/hatchet/pkg/client/loader"
|
||||
@@ -53,14 +54,27 @@ type ClientOpts struct {
|
||||
initWorkflows bool
|
||||
}
|
||||
|
||||
func defaultClientOpts() *ClientOpts {
|
||||
// read from environment variables and hostname by default
|
||||
func defaultClientOpts(cf *client.ClientConfigFile) *ClientOpts {
|
||||
var clientConfig *client.ClientConfig
|
||||
var err error
|
||||
|
||||
configLoader := &loader.ConfigLoader{}
|
||||
|
||||
clientConfig, err := configLoader.LoadClientConfig()
|
||||
if cf == nil {
|
||||
// read from environment variables and hostname by default
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
clientConfig, err = configLoader.LoadClientConfig()
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
} else {
|
||||
clientConfig, err = loader.GetClientConfigFromConfigFile(cf)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
logger := logger.NewDefaultLogger("client")
|
||||
@@ -88,6 +102,12 @@ func WithHostPort(host string, port int) ClientOpt {
|
||||
}
|
||||
}
|
||||
|
||||
func WithToken(token string) ClientOpt {
|
||||
return func(opts *ClientOpts) {
|
||||
opts.token = token
|
||||
}
|
||||
}
|
||||
|
||||
func InitWorkflows() ClientOpt {
|
||||
return func(opts *ClientOpts) {
|
||||
opts.initWorkflows = true
|
||||
@@ -113,12 +133,26 @@ type sharedClientOpts struct {
|
||||
|
||||
// New creates a new client instance.
|
||||
func New(fs ...ClientOpt) (Client, error) {
|
||||
opts := defaultClientOpts()
|
||||
opts := defaultClientOpts(nil)
|
||||
|
||||
for _, f := range fs {
|
||||
f(opts)
|
||||
}
|
||||
|
||||
return newFromOpts(opts)
|
||||
}
|
||||
|
||||
func NewFromConfigFile(cf *client.ClientConfigFile, fs ...ClientOpt) (Client, error) {
|
||||
opts := defaultClientOpts(cf)
|
||||
|
||||
for _, f := range fs {
|
||||
f(opts)
|
||||
}
|
||||
|
||||
return newFromOpts(opts)
|
||||
}
|
||||
|
||||
func newFromOpts(opts *ClientOpts) (Client, error) {
|
||||
// if no TLS, exit
|
||||
if opts.tls == nil {
|
||||
return nil, fmt.Errorf("tls config is required")
|
||||
|
||||
@@ -43,6 +43,14 @@ func LoadClientConfigFile(files ...[]byte) (*client.ClientConfigFile, error) {
|
||||
}
|
||||
|
||||
func GetClientConfigFromConfigFile(cf *client.ClientConfigFile) (res *client.ClientConfig, err error) {
|
||||
f := client.BindAllEnv
|
||||
|
||||
_, err = loaderutils.LoadConfigFromViper(f, cf)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load config from viper: %w", err)
|
||||
}
|
||||
|
||||
// if token is empty, throw an error
|
||||
if cf.Token == "" {
|
||||
return nil, fmt.Errorf("API token is required. Set it via the HATCHET_CLIENT_TOKEN environment variable.")
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "VcsProvider" AS ENUM ('GITHUB');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "StepRun" ADD COLUMN "callerFiles" JSONB,
|
||||
ADD COLUMN "gitRepoBranch" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "WorkflowRun" ADD COLUMN "gitRepoBranch" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "WorkflowDeploymentConfig" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"workflowId" UUID NOT NULL,
|
||||
"gitRepoName" TEXT NOT NULL,
|
||||
"gitRepoOwner" TEXT NOT NULL,
|
||||
"gitRepoBranch" TEXT NOT NULL,
|
||||
"githubAppInstallationId" UUID,
|
||||
|
||||
CONSTRAINT "WorkflowDeploymentConfig_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "StepRunResultArchive" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"stepRunId" UUID NOT NULL,
|
||||
"order" SMALLSERIAL NOT NULL,
|
||||
"input" JSONB,
|
||||
"output" JSONB,
|
||||
"error" TEXT,
|
||||
"startedAt" TIMESTAMP(3),
|
||||
"finishedAt" TIMESTAMP(3),
|
||||
"timeoutAt" TIMESTAMP(3),
|
||||
"cancelledAt" TIMESTAMP(3),
|
||||
"cancelledReason" TEXT,
|
||||
"cancelledError" TEXT,
|
||||
|
||||
CONSTRAINT "StepRunResultArchive_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "TenantVcsProvider" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"vcsProvider" "VcsProvider" NOT NULL,
|
||||
"config" JSONB,
|
||||
|
||||
CONSTRAINT "TenantVcsProvider_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GithubAppInstallation" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"githubAppOAuthId" UUID NOT NULL,
|
||||
"installationId" INTEGER NOT NULL,
|
||||
"accountName" TEXT NOT NULL,
|
||||
"accountId" INTEGER NOT NULL,
|
||||
"accountAvatarURL" TEXT,
|
||||
"installationSettingsURL" TEXT,
|
||||
"config" JSONB,
|
||||
"tenantId" UUID,
|
||||
"tenantVcsProviderId" UUID,
|
||||
|
||||
CONSTRAINT "GithubAppInstallation_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GithubAppOAuth" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"githubUserID" INTEGER NOT NULL,
|
||||
"accessToken" BYTEA NOT NULL,
|
||||
"refreshToken" BYTEA,
|
||||
"expiresAt" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "GithubAppOAuth_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GithubPullRequest" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"repositoryOwner" TEXT NOT NULL,
|
||||
"repositoryName" TEXT NOT NULL,
|
||||
"pullRequestID" INTEGER NOT NULL,
|
||||
"pullRequestTitle" TEXT NOT NULL,
|
||||
"pullRequestNumber" INTEGER NOT NULL,
|
||||
"pullRequestHeadBranch" TEXT NOT NULL,
|
||||
"pullRequestBaseBranch" TEXT NOT NULL,
|
||||
"pullRequestState" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "GithubPullRequest_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GithubPullRequestComment" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"pullRequestID" UUID NOT NULL,
|
||||
"moduleID" TEXT NOT NULL,
|
||||
"commentID" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "GithubPullRequestComment_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "GithubWebhook" (
|
||||
"id" UUID NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deletedAt" TIMESTAMP(3),
|
||||
"tenantId" UUID NOT NULL,
|
||||
"repositoryOwner" TEXT NOT NULL,
|
||||
"repositoryName" TEXT NOT NULL,
|
||||
"signingSecret" BYTEA NOT NULL,
|
||||
|
||||
CONSTRAINT "GithubWebhook_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_GithubAppInstallationToGithubWebhook" (
|
||||
"A" UUID NOT NULL,
|
||||
"B" UUID NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_GithubAppOAuthToUser" (
|
||||
"A" UUID NOT NULL,
|
||||
"B" UUID NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_GithubPullRequestToWorkflowRun" (
|
||||
"A" UUID NOT NULL,
|
||||
"B" UUID NOT NULL
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "WorkflowDeploymentConfig_id_key" ON "WorkflowDeploymentConfig"("id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "WorkflowDeploymentConfig_workflowId_key" ON "WorkflowDeploymentConfig"("workflowId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "StepRunResultArchive_id_key" ON "StepRunResultArchive"("id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "TenantVcsProvider_id_key" ON "TenantVcsProvider"("id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "TenantVcsProvider_tenantId_vcsProvider_key" ON "TenantVcsProvider"("tenantId", "vcsProvider");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubAppInstallation_id_key" ON "GithubAppInstallation"("id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubAppInstallation_installationId_accountId_key" ON "GithubAppInstallation"("installationId", "accountId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubAppOAuth_id_key" ON "GithubAppOAuth"("id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubAppOAuth_githubUserID_key" ON "GithubAppOAuth"("githubUserID");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubPullRequest_id_key" ON "GithubPullRequest"("id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubPullRequest_tenantId_repositoryOwner_repositoryName_p_key" ON "GithubPullRequest"("tenantId", "repositoryOwner", "repositoryName", "pullRequestNumber");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubPullRequestComment_id_key" ON "GithubPullRequestComment"("id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubWebhook_id_key" ON "GithubWebhook"("id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "GithubWebhook_tenantId_repositoryOwner_repositoryName_key" ON "GithubWebhook"("tenantId", "repositoryOwner", "repositoryName");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_GithubAppInstallationToGithubWebhook_AB_unique" ON "_GithubAppInstallationToGithubWebhook"("A", "B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_GithubAppInstallationToGithubWebhook_B_index" ON "_GithubAppInstallationToGithubWebhook"("B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_GithubAppOAuthToUser_AB_unique" ON "_GithubAppOAuthToUser"("A", "B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_GithubAppOAuthToUser_B_index" ON "_GithubAppOAuthToUser"("B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_GithubPullRequestToWorkflowRun_AB_unique" ON "_GithubPullRequestToWorkflowRun"("A", "B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_GithubPullRequestToWorkflowRun_B_index" ON "_GithubPullRequestToWorkflowRun"("B");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "WorkflowDeploymentConfig" ADD CONSTRAINT "WorkflowDeploymentConfig_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "Workflow"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "WorkflowDeploymentConfig" ADD CONSTRAINT "WorkflowDeploymentConfig_githubAppInstallationId_fkey" FOREIGN KEY ("githubAppInstallationId") REFERENCES "GithubAppInstallation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "StepRunResultArchive" ADD CONSTRAINT "StepRunResultArchive_stepRunId_fkey" FOREIGN KEY ("stepRunId") REFERENCES "StepRun"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "TenantVcsProvider" ADD CONSTRAINT "TenantVcsProvider_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubAppInstallation" ADD CONSTRAINT "GithubAppInstallation_githubAppOAuthId_fkey" FOREIGN KEY ("githubAppOAuthId") REFERENCES "GithubAppOAuth"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubAppInstallation" ADD CONSTRAINT "GithubAppInstallation_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubAppInstallation" ADD CONSTRAINT "GithubAppInstallation_tenantVcsProviderId_fkey" FOREIGN KEY ("tenantVcsProviderId") REFERENCES "TenantVcsProvider"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubPullRequest" ADD CONSTRAINT "GithubPullRequest_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubPullRequestComment" ADD CONSTRAINT "GithubPullRequestComment_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubPullRequestComment" ADD CONSTRAINT "GithubPullRequestComment_pullRequestID_fkey" FOREIGN KEY ("pullRequestID") REFERENCES "GithubPullRequest"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "GithubWebhook" ADD CONSTRAINT "GithubWebhook_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES "Tenant"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubAppInstallationToGithubWebhook" ADD CONSTRAINT "_GithubAppInstallationToGithubWebhook_A_fkey" FOREIGN KEY ("A") REFERENCES "GithubAppInstallation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubAppInstallationToGithubWebhook" ADD CONSTRAINT "_GithubAppInstallationToGithubWebhook_B_fkey" FOREIGN KEY ("B") REFERENCES "GithubWebhook"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubAppOAuthToUser" ADD CONSTRAINT "_GithubAppOAuthToUser_A_fkey" FOREIGN KEY ("A") REFERENCES "GithubAppOAuth"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubAppOAuthToUser" ADD CONSTRAINT "_GithubAppOAuthToUser_B_fkey" FOREIGN KEY ("B") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubPullRequestToWorkflowRun" ADD CONSTRAINT "_GithubPullRequestToWorkflowRun_A_fkey" FOREIGN KEY ("A") REFERENCES "GithubPullRequest"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_GithubPullRequestToWorkflowRun" ADD CONSTRAINT "_GithubPullRequestToWorkflowRun_B_fkey" FOREIGN KEY ("B") REFERENCES "WorkflowRun"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
INSERT INTO
|
||||
"Tenant" (
|
||||
"id",
|
||||
"createdAt",
|
||||
"updatedAt",
|
||||
"deletedAt",
|
||||
"name",
|
||||
"slug"
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
'8d420720-ef03-41dc-9c73-1c93f276db97',
|
||||
CURRENT_TIMESTAMP,
|
||||
CURRENT_TIMESTAMP,
|
||||
NULL,
|
||||
'internal',
|
||||
'internal'
|
||||
) ON CONFLICT DO NOTHING;
|
||||
|
||||
CREATE OR REPLACE FUNCTION prevent_internal_name_or_slug()
|
||||
RETURNS trigger AS $$
|
||||
BEGIN
|
||||
IF NEW."name" = 'internal' OR NEW."slug" = 'internal' THEN
|
||||
RAISE EXCEPTION 'Values "internal" for "name" or "slug" are not allowed.';
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER check_name_or_slug_before_insert_or_update
|
||||
BEFORE INSERT OR UPDATE ON "Tenant"
|
||||
FOR EACH ROW EXECUTE FUNCTION prevent_internal_name_or_slug();
|
||||
+274
-20
@@ -33,7 +33,8 @@ model User {
|
||||
// the user sessions
|
||||
sessions UserSession[]
|
||||
|
||||
memberships TenantMember[]
|
||||
memberships TenantMember[]
|
||||
githubAppOAuths GithubAppOAuth[]
|
||||
}
|
||||
|
||||
model UserOAuth {
|
||||
@@ -101,24 +102,29 @@ model Tenant {
|
||||
name String
|
||||
slug String @unique
|
||||
|
||||
events Event[]
|
||||
workflows Workflow[]
|
||||
jobs Job[]
|
||||
steps Step[]
|
||||
triggers WorkflowTriggers[]
|
||||
workflowRuns WorkflowRun[]
|
||||
workflowRunTriggers WorkflowRunTriggeredBy[]
|
||||
jobRuns JobRun[]
|
||||
jobRunLookupDatas JobRunLookupData[]
|
||||
stepRuns StepRun[]
|
||||
workers Worker[]
|
||||
members TenantMember[]
|
||||
workflowTags WorkflowTag[]
|
||||
actions Action[]
|
||||
services Service[]
|
||||
invites TenantInviteLink[]
|
||||
apiTokens APIToken[]
|
||||
groupKeyRuns GetGroupKeyRun[]
|
||||
events Event[]
|
||||
workflows Workflow[]
|
||||
jobs Job[]
|
||||
steps Step[]
|
||||
triggers WorkflowTriggers[]
|
||||
workflowRuns WorkflowRun[]
|
||||
workflowRunTriggers WorkflowRunTriggeredBy[]
|
||||
jobRuns JobRun[]
|
||||
jobRunLookupDatas JobRunLookupData[]
|
||||
stepRuns StepRun[]
|
||||
workers Worker[]
|
||||
members TenantMember[]
|
||||
workflowTags WorkflowTag[]
|
||||
actions Action[]
|
||||
services Service[]
|
||||
invites TenantInviteLink[]
|
||||
apiTokens APIToken[]
|
||||
groupKeyRuns GetGroupKeyRun[]
|
||||
vcsProviders TenantVcsProvider[]
|
||||
githubAppInstallations GithubAppInstallation[]
|
||||
githubPullRequests GithubPullRequest[]
|
||||
githubPullRequestComments GithubPullRequestComment[]
|
||||
githubWebhooks GithubWebhook[]
|
||||
}
|
||||
|
||||
enum TenantMemberRole {
|
||||
@@ -263,12 +269,33 @@ model Workflow {
|
||||
versions WorkflowVersion[]
|
||||
|
||||
// the tags for this workflow
|
||||
tags WorkflowTag[]
|
||||
tags WorkflowTag[]
|
||||
deploymentConfig WorkflowDeploymentConfig?
|
||||
|
||||
// workflow names are unique per tenant
|
||||
@@unique([tenantId, name])
|
||||
}
|
||||
|
||||
model WorkflowDeploymentConfig {
|
||||
// base fields
|
||||
id String @id @unique @default(uuid()) @db.Uuid
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
// the parent workflow
|
||||
workflow Workflow @relation(fields: [workflowId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
workflowId String @unique @db.Uuid
|
||||
|
||||
gitRepoName String
|
||||
gitRepoOwner String
|
||||
gitRepoBranch String
|
||||
|
||||
// Github-related deployment config
|
||||
githubAppInstallation GithubAppInstallation? @relation(fields: [githubAppInstallationId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
githubAppInstallationId String? @db.Uuid
|
||||
}
|
||||
|
||||
model WorkflowVersion {
|
||||
// base fields
|
||||
id String @id @unique @default(uuid()) @db.Uuid
|
||||
@@ -551,6 +578,11 @@ model WorkflowRun {
|
||||
|
||||
// the run finished at
|
||||
finishedAt DateTime?
|
||||
|
||||
// (optional) the branch for the github repo
|
||||
gitRepoBranch String?
|
||||
|
||||
pullRequests GithubPullRequest[]
|
||||
}
|
||||
|
||||
model GetGroupKeyRun {
|
||||
@@ -809,6 +841,54 @@ model StepRun {
|
||||
|
||||
// errors while cancelling the run
|
||||
cancelledError String?
|
||||
|
||||
// a map of override values to caller files for the step run
|
||||
callerFiles Json?
|
||||
|
||||
// the github branch that this is running on
|
||||
gitRepoBranch String?
|
||||
|
||||
archivedResults StepRunResultArchive[]
|
||||
}
|
||||
|
||||
model StepRunResultArchive {
|
||||
id String @id @unique @default(uuid()) @db.Uuid
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
// the parent step run
|
||||
stepRun StepRun @relation(fields: [stepRunId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
stepRunId String @db.Uuid
|
||||
|
||||
order Int @default(autoincrement()) @db.SmallInt
|
||||
|
||||
// the run input
|
||||
input Json?
|
||||
|
||||
// the run output
|
||||
output Json?
|
||||
|
||||
// the run error
|
||||
error String?
|
||||
|
||||
// the run started at
|
||||
startedAt DateTime?
|
||||
|
||||
// the run finished at
|
||||
finishedAt DateTime?
|
||||
|
||||
// the run timeout at
|
||||
timeoutAt DateTime?
|
||||
|
||||
// the run cancelled at
|
||||
cancelledAt DateTime?
|
||||
|
||||
// the reason for why the run was cancelled
|
||||
cancelledReason String?
|
||||
|
||||
// errors while cancelling the run
|
||||
cancelledError String?
|
||||
}
|
||||
|
||||
model Dispatcher {
|
||||
@@ -909,3 +989,177 @@ model Service {
|
||||
|
||||
@@unique([tenantId, name])
|
||||
}
|
||||
|
||||
enum VcsProvider {
|
||||
GITHUB
|
||||
}
|
||||
|
||||
model TenantVcsProvider {
|
||||
// base fields
|
||||
id String @id @unique @default(uuid()) @db.Uuid
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
// the parent tenant
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
tenantId String @db.Uuid
|
||||
|
||||
vcsProvider VcsProvider
|
||||
|
||||
// the provider name
|
||||
ghInstallations GithubAppInstallation[]
|
||||
|
||||
// the provider's configuration
|
||||
config Json?
|
||||
|
||||
@@unique([tenantId, vcsProvider])
|
||||
}
|
||||
|
||||
model GithubAppInstallation {
|
||||
// base fields
|
||||
id String @id @unique @default(uuid()) @db.Uuid
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
// the oauth id for the user that linked this installation
|
||||
githubAppOAuth GithubAppOAuth @relation(fields: [githubAppOAuthId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
githubAppOAuthId String @db.Uuid
|
||||
|
||||
// the installation id
|
||||
installationId Int
|
||||
accountName String
|
||||
accountId Int
|
||||
|
||||
// optionals
|
||||
accountAvatarURL String?
|
||||
installationSettingsURL String?
|
||||
|
||||
// the installation's configuration
|
||||
config Json?
|
||||
|
||||
webhooks GithubWebhook[]
|
||||
deploymentConfigs WorkflowDeploymentConfig[]
|
||||
Tenant Tenant? @relation(fields: [tenantId], references: [id])
|
||||
tenantId String? @db.Uuid
|
||||
TenantVcsProvider TenantVcsProvider? @relation(fields: [tenantVcsProviderId], references: [id])
|
||||
tenantVcsProviderId String? @db.Uuid
|
||||
|
||||
@@unique([installationId, accountId])
|
||||
}
|
||||
|
||||
model GithubAppOAuth {
|
||||
// base fields
|
||||
id String @id @unique @default(uuid()) @db.Uuid
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
// the oauth provider's user id
|
||||
githubUserID Int @unique
|
||||
|
||||
// a list of users this github account is linked to
|
||||
users User[]
|
||||
|
||||
// a list of installations this github account is linked to
|
||||
installations GithubAppInstallation[]
|
||||
|
||||
// the oauth provider's access token
|
||||
accessToken Bytes @db.ByteA
|
||||
|
||||
// the oauth provider's refresh token
|
||||
refreshToken Bytes? @db.ByteA
|
||||
|
||||
// the oauth provider's expiry time
|
||||
expiresAt DateTime?
|
||||
}
|
||||
|
||||
model GithubPullRequest {
|
||||
// base fields
|
||||
id String @id @unique @default(uuid()) @db.Uuid
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
// the parent tenant
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
tenantId String @db.Uuid
|
||||
|
||||
// the repository owner
|
||||
repositoryOwner String
|
||||
|
||||
// the repository name
|
||||
repositoryName String
|
||||
|
||||
// the pull request id
|
||||
pullRequestID Int
|
||||
|
||||
// the pull request title
|
||||
pullRequestTitle String
|
||||
|
||||
// the pull request number
|
||||
pullRequestNumber Int
|
||||
|
||||
// the pull request head branch
|
||||
pullRequestHeadBranch String
|
||||
|
||||
// the pull request base branch
|
||||
pullRequestBaseBranch String
|
||||
|
||||
// the pull request state
|
||||
pullRequestState String
|
||||
|
||||
// the pull request comments
|
||||
pullRequestComments GithubPullRequestComment[]
|
||||
workflowRuns WorkflowRun[]
|
||||
|
||||
@@unique([tenantId, repositoryOwner, repositoryName, pullRequestNumber])
|
||||
}
|
||||
|
||||
model GithubPullRequestComment {
|
||||
// base fields
|
||||
id String @id @unique @default(uuid()) @db.Uuid
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
// the parent tenant
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
tenantId String @db.Uuid
|
||||
|
||||
pullRequest GithubPullRequest @relation(fields: [pullRequestID], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
pullRequestID String @db.Uuid
|
||||
|
||||
// the module id
|
||||
moduleID String
|
||||
|
||||
// the comment id
|
||||
commentID Int
|
||||
}
|
||||
|
||||
model GithubWebhook {
|
||||
// base fields
|
||||
id String @id @unique @default(uuid()) @db.Uuid
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @default(now()) @updatedAt
|
||||
deletedAt DateTime?
|
||||
|
||||
// the parent tenant
|
||||
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
tenantId String @db.Uuid
|
||||
|
||||
// the repository owner
|
||||
repositoryOwner String
|
||||
|
||||
// the repository name
|
||||
repositoryName String
|
||||
|
||||
// the webhook signing secret
|
||||
signingSecret Bytes @db.ByteA
|
||||
|
||||
// the webhook's installations
|
||||
installations GithubAppInstallation[]
|
||||
|
||||
@@unique([tenantId, repositoryOwner, repositoryName])
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ class MyWorkflow:
|
||||
|
||||
@hatchet.step()
|
||||
def step1(self, context : Context):
|
||||
context.overrides("test", "test")
|
||||
|
||||
print("executed step1", context.workflow_input())
|
||||
return {
|
||||
"step1": "step1",
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
import inspect
|
||||
from multiprocessing import Event
|
||||
import os
|
||||
from .clients.dispatcher import Action, DispatcherClient
|
||||
from .dispatcher_pb2 import OverridesData
|
||||
from .logger import logger
|
||||
import json
|
||||
|
||||
def get_caller_file_path():
|
||||
caller_frame = inspect.stack()[2]
|
||||
|
||||
return caller_frame.filename
|
||||
|
||||
class Context:
|
||||
def __init__(self, action: Action, client: DispatcherClient):
|
||||
self.data = json.loads(action.action_payload)
|
||||
@@ -46,11 +53,14 @@ class Context:
|
||||
if name in self.overrides_data:
|
||||
return self.overrides_data[name]
|
||||
|
||||
caller_file = get_caller_file_path()
|
||||
|
||||
self.client.put_overrides_data(
|
||||
OverridesData(
|
||||
stepRunId=self.stepRunId,
|
||||
path=name,
|
||||
value=json.dumps(default)
|
||||
value=json.dumps(default),
|
||||
callerFilename=caller_file
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -15,9 +15,7 @@ _sym_db = _symbol_database.Default()
|
||||
from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2
|
||||
|
||||
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x64ispatcher.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"N\n\x15WorkerRegisterRequest\x12\x12\n\nworkerName\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x63tions\x18\x02 \x03(\t\x12\x10\n\x08services\x18\x03 \x03(\t\"P\n\x16WorkerRegisterResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\x12\x12\n\nworkerName\x18\x03 \x01(\t\"\xf2\x01\n\x0e\x41ssignedAction\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x15\n\rworkflowRunId\x18\x02 \x01(\t\x12\x18\n\x10getGroupKeyRunId\x18\x03 \x01(\t\x12\r\n\x05jobId\x18\x04 \x01(\t\x12\x0f\n\x07jobName\x18\x05 \x01(\t\x12\x10\n\x08jobRunId\x18\x06 \x01(\t\x12\x0e\n\x06stepId\x18\x07 \x01(\t\x12\x11\n\tstepRunId\x18\x08 \x01(\t\x12\x10\n\x08\x61\x63tionId\x18\t \x01(\t\x12\x1f\n\nactionType\x18\n \x01(\x0e\x32\x0b.ActionType\x12\x15\n\ractionPayload\x18\x0b \x01(\t\"\'\n\x13WorkerListenRequest\x12\x10\n\x08workerId\x18\x01 \x01(\t\",\n\x18WorkerUnsubscribeRequest\x12\x10\n\x08workerId\x18\x01 \x01(\t\"?\n\x19WorkerUnsubscribeResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\"\xe1\x01\n\x13GroupKeyActionEvent\x12\x10\n\x08workerId\x18\x01 \x01(\t\x12\x15\n\rworkflowRunId\x18\x02 \x01(\t\x12\x18\n\x10getGroupKeyRunId\x18\x03 \x01(\t\x12\x10\n\x08\x61\x63tionId\x18\x04 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12+\n\teventType\x18\x06 \x01(\x0e\x32\x18.GroupKeyActionEventType\x12\x14\n\x0c\x65ventPayload\x18\x07 \x01(\t\"\xec\x01\n\x0fStepActionEvent\x12\x10\n\x08workerId\x18\x01 \x01(\t\x12\r\n\x05jobId\x18\x02 \x01(\t\x12\x10\n\x08jobRunId\x18\x03 \x01(\t\x12\x0e\n\x06stepId\x18\x04 \x01(\t\x12\x11\n\tstepRunId\x18\x05 \x01(\t\x12\x10\n\x08\x61\x63tionId\x18\x06 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\teventType\x18\x08 \x01(\x0e\x32\x14.StepActionEventType\x12\x14\n\x0c\x65ventPayload\x18\t \x01(\t\"9\n\x13\x41\x63tionEventResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\"9\n SubscribeToWorkflowEventsRequest\x12\x15\n\rworkflowRunId\x18\x01 \x01(\t\"\xd0\x01\n\rWorkflowEvent\x12\x15\n\rworkflowRunId\x18\x01 \x01(\t\x12#\n\x0cresourceType\x18\x02 \x01(\x0e\x32\r.ResourceType\x12%\n\teventType\x18\x03 \x01(\x0e\x32\x12.ResourceEventType\x12\x12\n\nresourceId\x18\x04 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x14\n\x0c\x65ventPayload\x18\x06 \x01(\t\"?\n\rOverridesData\x12\x11\n\tstepRunId\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\"\x17\n\x15OverridesDataResponse*N\n\nActionType\x12\x12\n\x0eSTART_STEP_RUN\x10\x00\x12\x13\n\x0f\x43\x41NCEL_STEP_RUN\x10\x01\x12\x17\n\x13START_GET_GROUP_KEY\x10\x02*\xa2\x01\n\x17GroupKeyActionEventType\x12 \n\x1cGROUP_KEY_EVENT_TYPE_UNKNOWN\x10\x00\x12 \n\x1cGROUP_KEY_EVENT_TYPE_STARTED\x10\x01\x12\"\n\x1eGROUP_KEY_EVENT_TYPE_COMPLETED\x10\x02\x12\x1f\n\x1bGROUP_KEY_EVENT_TYPE_FAILED\x10\x03*\x8a\x01\n\x13StepActionEventType\x12\x1b\n\x17STEP_EVENT_TYPE_UNKNOWN\x10\x00\x12\x1b\n\x17STEP_EVENT_TYPE_STARTED\x10\x01\x12\x1d\n\x19STEP_EVENT_TYPE_COMPLETED\x10\x02\x12\x1a\n\x16STEP_EVENT_TYPE_FAILED\x10\x03*e\n\x0cResourceType\x12\x19\n\x15RESOURCE_TYPE_UNKNOWN\x10\x00\x12\x1a\n\x16RESOURCE_TYPE_STEP_RUN\x10\x01\x12\x1e\n\x1aRESOURCE_TYPE_WORKFLOW_RUN\x10\x02*\xde\x01\n\x11ResourceEventType\x12\x1f\n\x1bRESOURCE_EVENT_TYPE_UNKNOWN\x10\x00\x12\x1f\n\x1bRESOURCE_EVENT_TYPE_STARTED\x10\x01\x12!\n\x1dRESOURCE_EVENT_TYPE_COMPLETED\x10\x02\x12\x1e\n\x1aRESOURCE_EVENT_TYPE_FAILED\x10\x03\x12!\n\x1dRESOURCE_EVENT_TYPE_CANCELLED\x10\x04\x12!\n\x1dRESOURCE_EVENT_TYPE_TIMED_OUT\x10\x05\x32\xe4\x03\n\nDispatcher\x12=\n\x08Register\x12\x16.WorkerRegisterRequest\x1a\x17.WorkerRegisterResponse\"\x00\x12\x33\n\x06Listen\x12\x14.WorkerListenRequest\x1a\x0f.AssignedAction\"\x00\x30\x01\x12R\n\x19SubscribeToWorkflowEvents\x12!.SubscribeToWorkflowEventsRequest\x1a\x0e.WorkflowEvent\"\x00\x30\x01\x12?\n\x13SendStepActionEvent\x12\x10.StepActionEvent\x1a\x14.ActionEventResponse\"\x00\x12G\n\x17SendGroupKeyActionEvent\x12\x14.GroupKeyActionEvent\x1a\x14.ActionEventResponse\"\x00\x12<\n\x10PutOverridesData\x12\x0e.OverridesData\x1a\x16.OverridesDataResponse\"\x00\x12\x46\n\x0bUnsubscribe\x12\x19.WorkerUnsubscribeRequest\x1a\x1a.WorkerUnsubscribeResponse\"\x00\x42GZEgithub.com/hatchet-dev/hatchet/internal/services/dispatcher/contractsb\x06proto3')
|
||||
|
||||
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x64ispatcher.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"N\n\x15WorkerRegisterRequest\x12\x12\n\nworkerName\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x63tions\x18\x02 \x03(\t\x12\x10\n\x08services\x18\x03 \x03(\t\"P\n\x16WorkerRegisterResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\x12\x12\n\nworkerName\x18\x03 \x01(\t\"\xf2\x01\n\x0e\x41ssignedAction\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x15\n\rworkflowRunId\x18\x02 \x01(\t\x12\x18\n\x10getGroupKeyRunId\x18\x03 \x01(\t\x12\r\n\x05jobId\x18\x04 \x01(\t\x12\x0f\n\x07jobName\x18\x05 \x01(\t\x12\x10\n\x08jobRunId\x18\x06 \x01(\t\x12\x0e\n\x06stepId\x18\x07 \x01(\t\x12\x11\n\tstepRunId\x18\x08 \x01(\t\x12\x10\n\x08\x61\x63tionId\x18\t \x01(\t\x12\x1f\n\nactionType\x18\n \x01(\x0e\x32\x0b.ActionType\x12\x15\n\ractionPayload\x18\x0b \x01(\t\"\'\n\x13WorkerListenRequest\x12\x10\n\x08workerId\x18\x01 \x01(\t\",\n\x18WorkerUnsubscribeRequest\x12\x10\n\x08workerId\x18\x01 \x01(\t\"?\n\x19WorkerUnsubscribeResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\"\xe1\x01\n\x13GroupKeyActionEvent\x12\x10\n\x08workerId\x18\x01 \x01(\t\x12\x15\n\rworkflowRunId\x18\x02 \x01(\t\x12\x18\n\x10getGroupKeyRunId\x18\x03 \x01(\t\x12\x10\n\x08\x61\x63tionId\x18\x04 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12+\n\teventType\x18\x06 \x01(\x0e\x32\x18.GroupKeyActionEventType\x12\x14\n\x0c\x65ventPayload\x18\x07 \x01(\t\"\xec\x01\n\x0fStepActionEvent\x12\x10\n\x08workerId\x18\x01 \x01(\t\x12\r\n\x05jobId\x18\x02 \x01(\t\x12\x10\n\x08jobRunId\x18\x03 \x01(\t\x12\x0e\n\x06stepId\x18\x04 \x01(\t\x12\x11\n\tstepRunId\x18\x05 \x01(\t\x12\x10\n\x08\x61\x63tionId\x18\x06 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\teventType\x18\x08 \x01(\x0e\x32\x14.StepActionEventType\x12\x14\n\x0c\x65ventPayload\x18\t \x01(\t\"9\n\x13\x41\x63tionEventResponse\x12\x10\n\x08tenantId\x18\x01 \x01(\t\x12\x10\n\x08workerId\x18\x02 \x01(\t\"9\n SubscribeToWorkflowEventsRequest\x12\x15\n\rworkflowRunId\x18\x01 \x01(\t\"\xe0\x01\n\rWorkflowEvent\x12\x15\n\rworkflowRunId\x18\x01 \x01(\t\x12#\n\x0cresourceType\x18\x02 \x01(\x0e\x32\r.ResourceType\x12%\n\teventType\x18\x03 \x01(\x0e\x32\x12.ResourceEventType\x12\x12\n\nresourceId\x18\x04 \x01(\t\x12\x32\n\x0e\x65ventTimestamp\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x14\n\x0c\x65ventPayload\x18\x06 \x01(\t\x12\x0e\n\x06hangup\x18\x07 \x01(\x08\"W\n\rOverridesData\x12\x11\n\tstepRunId\x18\x01 \x01(\t\x12\x0c\n\x04path\x18\x02 \x01(\t\x12\r\n\x05value\x18\x03 \x01(\t\x12\x16\n\x0e\x63\x61llerFilename\x18\x04 \x01(\t\"\x17\n\x15OverridesDataResponse*N\n\nActionType\x12\x12\n\x0eSTART_STEP_RUN\x10\x00\x12\x13\n\x0f\x43\x41NCEL_STEP_RUN\x10\x01\x12\x17\n\x13START_GET_GROUP_KEY\x10\x02*\xa2\x01\n\x17GroupKeyActionEventType\x12 \n\x1cGROUP_KEY_EVENT_TYPE_UNKNOWN\x10\x00\x12 \n\x1cGROUP_KEY_EVENT_TYPE_STARTED\x10\x01\x12\"\n\x1eGROUP_KEY_EVENT_TYPE_COMPLETED\x10\x02\x12\x1f\n\x1bGROUP_KEY_EVENT_TYPE_FAILED\x10\x03*\x8a\x01\n\x13StepActionEventType\x12\x1b\n\x17STEP_EVENT_TYPE_UNKNOWN\x10\x00\x12\x1b\n\x17STEP_EVENT_TYPE_STARTED\x10\x01\x12\x1d\n\x19STEP_EVENT_TYPE_COMPLETED\x10\x02\x12\x1a\n\x16STEP_EVENT_TYPE_FAILED\x10\x03*e\n\x0cResourceType\x12\x19\n\x15RESOURCE_TYPE_UNKNOWN\x10\x00\x12\x1a\n\x16RESOURCE_TYPE_STEP_RUN\x10\x01\x12\x1e\n\x1aRESOURCE_TYPE_WORKFLOW_RUN\x10\x02*\xde\x01\n\x11ResourceEventType\x12\x1f\n\x1bRESOURCE_EVENT_TYPE_UNKNOWN\x10\x00\x12\x1f\n\x1bRESOURCE_EVENT_TYPE_STARTED\x10\x01\x12!\n\x1dRESOURCE_EVENT_TYPE_COMPLETED\x10\x02\x12\x1e\n\x1aRESOURCE_EVENT_TYPE_FAILED\x10\x03\x12!\n\x1dRESOURCE_EVENT_TYPE_CANCELLED\x10\x04\x12!\n\x1dRESOURCE_EVENT_TYPE_TIMED_OUT\x10\x05\x32\xe4\x03\n\nDispatcher\x12=\n\x08Register\x12\x16.WorkerRegisterRequest\x1a\x17.WorkerRegisterResponse\"\x00\x12\x33\n\x06Listen\x12\x14.WorkerListenRequest\x1a\x0f.AssignedAction\"\x00\x30\x01\x12R\n\x19SubscribeToWorkflowEvents\x12!.SubscribeToWorkflowEventsRequest\x1a\x0e.WorkflowEvent\"\x00\x30\x01\x12?\n\x13SendStepActionEvent\x12\x10.StepActionEvent\x1a\x14.ActionEventResponse\"\x00\x12G\n\x17SendGroupKeyActionEvent\x12\x14.GroupKeyActionEvent\x1a\x14.ActionEventResponse\"\x00\x12<\n\x10PutOverridesData\x12\x0e.OverridesData\x1a\x16.OverridesDataResponse\"\x00\x12\x46\n\x0bUnsubscribe\x12\x19.WorkerUnsubscribeRequest\x1a\x1a.WorkerUnsubscribeResponse\"\x00\x42GZEgithub.com/hatchet-dev/hatchet/internal/services/dispatcher/contractsb\x06proto3')
|
||||
|
||||
_globals = globals()
|
||||
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
||||
@@ -25,16 +23,16 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'dispatcher_pb2', _globals)
|
||||
if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
_globals['DESCRIPTOR']._options = None
|
||||
_globals['DESCRIPTOR']._serialized_options = b'ZEgithub.com/hatchet-dev/hatchet/internal/services/dispatcher/contracts'
|
||||
_globals['_ACTIONTYPE']._serialized_start=1498
|
||||
_globals['_ACTIONTYPE']._serialized_end=1576
|
||||
_globals['_GROUPKEYACTIONEVENTTYPE']._serialized_start=1579
|
||||
_globals['_GROUPKEYACTIONEVENTTYPE']._serialized_end=1741
|
||||
_globals['_STEPACTIONEVENTTYPE']._serialized_start=1744
|
||||
_globals['_STEPACTIONEVENTTYPE']._serialized_end=1882
|
||||
_globals['_RESOURCETYPE']._serialized_start=1884
|
||||
_globals['_RESOURCETYPE']._serialized_end=1985
|
||||
_globals['_RESOURCEEVENTTYPE']._serialized_start=1988
|
||||
_globals['_RESOURCEEVENTTYPE']._serialized_end=2210
|
||||
_globals['_ACTIONTYPE']._serialized_start=1538
|
||||
_globals['_ACTIONTYPE']._serialized_end=1616
|
||||
_globals['_GROUPKEYACTIONEVENTTYPE']._serialized_start=1619
|
||||
_globals['_GROUPKEYACTIONEVENTTYPE']._serialized_end=1781
|
||||
_globals['_STEPACTIONEVENTTYPE']._serialized_start=1784
|
||||
_globals['_STEPACTIONEVENTTYPE']._serialized_end=1922
|
||||
_globals['_RESOURCETYPE']._serialized_start=1924
|
||||
_globals['_RESOURCETYPE']._serialized_end=2025
|
||||
_globals['_RESOURCEEVENTTYPE']._serialized_start=2028
|
||||
_globals['_RESOURCEEVENTTYPE']._serialized_end=2250
|
||||
_globals['_WORKERREGISTERREQUEST']._serialized_start=53
|
||||
_globals['_WORKERREGISTERREQUEST']._serialized_end=131
|
||||
_globals['_WORKERREGISTERRESPONSE']._serialized_start=133
|
||||
@@ -56,11 +54,11 @@ if _descriptor._USE_C_DESCRIPTORS == False:
|
||||
_globals['_SUBSCRIBETOWORKFLOWEVENTSREQUEST']._serialized_start=1138
|
||||
_globals['_SUBSCRIBETOWORKFLOWEVENTSREQUEST']._serialized_end=1195
|
||||
_globals['_WORKFLOWEVENT']._serialized_start=1198
|
||||
_globals['_WORKFLOWEVENT']._serialized_end=1406
|
||||
_globals['_OVERRIDESDATA']._serialized_start=1408
|
||||
_globals['_OVERRIDESDATA']._serialized_end=1471
|
||||
_globals['_OVERRIDESDATARESPONSE']._serialized_start=1473
|
||||
_globals['_OVERRIDESDATARESPONSE']._serialized_end=1496
|
||||
_globals['_DISPATCHER']._serialized_start=2213
|
||||
_globals['_DISPATCHER']._serialized_end=2697
|
||||
_globals['_WORKFLOWEVENT']._serialized_end=1422
|
||||
_globals['_OVERRIDESDATA']._serialized_start=1424
|
||||
_globals['_OVERRIDESDATA']._serialized_end=1511
|
||||
_globals['_OVERRIDESDATARESPONSE']._serialized_start=1513
|
||||
_globals['_OVERRIDESDATARESPONSE']._serialized_end=1536
|
||||
_globals['_DISPATCHER']._serialized_start=2253
|
||||
_globals['_DISPATCHER']._serialized_end=2737
|
||||
# @@protoc_insertion_point(module_scope)
|
||||
|
||||
@@ -197,17 +197,20 @@ class WorkflowEvent(_message.Message):
|
||||
resourceId: str
|
||||
eventTimestamp: _timestamp_pb2.Timestamp
|
||||
eventPayload: str
|
||||
def __init__(self, workflowRunId: _Optional[str] = ..., resourceType: _Optional[_Union[ResourceType, str]] = ..., eventType: _Optional[_Union[ResourceEventType, str]] = ..., resourceId: _Optional[str] = ..., eventTimestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., eventPayload: _Optional[str] = ...) -> None: ...
|
||||
hangup: bool
|
||||
def __init__(self, workflowRunId: _Optional[str] = ..., resourceType: _Optional[_Union[ResourceType, str]] = ..., eventType: _Optional[_Union[ResourceEventType, str]] = ..., resourceId: _Optional[str] = ..., eventTimestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., eventPayload: _Optional[str] = ..., hangup: bool = ...) -> None: ...
|
||||
|
||||
class OverridesData(_message.Message):
|
||||
__slots__ = ("stepRunId", "path", "value")
|
||||
__slots__ = ("stepRunId", "path", "value", "callerFilename")
|
||||
STEPRUNID_FIELD_NUMBER: _ClassVar[int]
|
||||
PATH_FIELD_NUMBER: _ClassVar[int]
|
||||
VALUE_FIELD_NUMBER: _ClassVar[int]
|
||||
CALLERFILENAME_FIELD_NUMBER: _ClassVar[int]
|
||||
stepRunId: str
|
||||
path: str
|
||||
value: str
|
||||
def __init__(self, stepRunId: _Optional[str] = ..., path: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ...
|
||||
callerFilename: str
|
||||
def __init__(self, stepRunId: _Optional[str] = ..., path: _Optional[str] = ..., value: _Optional[str] = ..., callerFilename: _Optional[str] = ...) -> None: ...
|
||||
|
||||
class OverridesDataResponse(_message.Message):
|
||||
__slots__ = ()
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
AcceptInviteRequest,
|
||||
CreateAPITokenRequest,
|
||||
CreateAPITokenResponse,
|
||||
CreatePullRequestFromStepRun,
|
||||
CreateTenantInviteRequest,
|
||||
CreateTenantRequest,
|
||||
EventData,
|
||||
@@ -25,7 +26,11 @@ import {
|
||||
EventOrderByDirection,
|
||||
EventOrderByField,
|
||||
EventSearch,
|
||||
LinkGithubRepositoryRequest,
|
||||
ListAPITokensResponse,
|
||||
ListGithubAppInstallationsResponse,
|
||||
ListGithubBranchesResponse,
|
||||
ListGithubReposResponse,
|
||||
RejectInviteRequest,
|
||||
ReplayEventRequest,
|
||||
RerunStepRunRequest,
|
||||
@@ -89,11 +94,11 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
|
||||
* @description Starts the OAuth flow
|
||||
*
|
||||
* @tags User
|
||||
* @name UserUpdateOauthStart
|
||||
* @name UserUpdateGoogleOauthStart
|
||||
* @summary Start OAuth flow
|
||||
* @request GET:/api/v1/users/google/start
|
||||
*/
|
||||
userUpdateOauthStart = (params: RequestParams = {}) =>
|
||||
userUpdateGoogleOauthStart = (params: RequestParams = {}) =>
|
||||
this.request<any, void>({
|
||||
path: `/api/v1/users/google/start`,
|
||||
method: 'GET',
|
||||
@@ -103,16 +108,76 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
|
||||
* @description Completes the OAuth flow
|
||||
*
|
||||
* @tags User
|
||||
* @name UserUpdateOauthCallback
|
||||
* @name UserUpdateGoogleOauthCallback
|
||||
* @summary Complete OAuth flow
|
||||
* @request GET:/api/v1/users/google/callback
|
||||
*/
|
||||
userUpdateOauthCallback = (params: RequestParams = {}) =>
|
||||
userUpdateGoogleOauthCallback = (params: RequestParams = {}) =>
|
||||
this.request<any, void>({
|
||||
path: `/api/v1/users/google/callback`,
|
||||
method: 'GET',
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Starts the OAuth flow
|
||||
*
|
||||
* @tags User
|
||||
* @name UserUpdateGithubOauthStart
|
||||
* @summary Start OAuth flow
|
||||
* @request GET:/api/v1/users/github/start
|
||||
* @secure
|
||||
*/
|
||||
userUpdateGithubOauthStart = (params: RequestParams = {}) =>
|
||||
this.request<any, void>({
|
||||
path: `/api/v1/users/github/start`,
|
||||
method: 'GET',
|
||||
secure: true,
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Completes the OAuth flow
|
||||
*
|
||||
* @tags User
|
||||
* @name UserUpdateGithubOauthCallback
|
||||
* @summary Complete OAuth flow
|
||||
* @request GET:/api/v1/users/github/callback
|
||||
* @secure
|
||||
*/
|
||||
userUpdateGithubOauthCallback = (params: RequestParams = {}) =>
|
||||
this.request<any, void>({
|
||||
path: `/api/v1/users/github/callback`,
|
||||
method: 'GET',
|
||||
secure: true,
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Github App global webhook
|
||||
*
|
||||
* @tags Github
|
||||
* @name GithubUpdateGlobalWebhook
|
||||
* @summary Github app global webhook
|
||||
* @request POST:/api/v1/github/webhook
|
||||
*/
|
||||
githubUpdateGlobalWebhook = (params: RequestParams = {}) =>
|
||||
this.request<void, APIErrors>({
|
||||
path: `/api/v1/github/webhook`,
|
||||
method: 'POST',
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Github App tenant webhook
|
||||
*
|
||||
* @tags Github
|
||||
* @name GithubUpdateTenantWebhook
|
||||
* @summary Github app tenant webhook
|
||||
* @request POST:/api/v1/github/webhook/{webhook}
|
||||
*/
|
||||
githubUpdateTenantWebhook = (webhook: string, params: RequestParams = {}) =>
|
||||
this.request<void, APIErrors>({
|
||||
path: `/api/v1/github/webhook/${webhook}`,
|
||||
method: 'POST',
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Gets the current user
|
||||
*
|
||||
@@ -624,6 +689,52 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
|
||||
format: 'json',
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Link a github repository to a workflow
|
||||
*
|
||||
* @tags Workflow
|
||||
* @name WorkflowUpdateLinkGithub
|
||||
* @summary Link github repository
|
||||
* @request POST:/api/v1/workflows/{workflow}/link-github
|
||||
* @secure
|
||||
*/
|
||||
workflowUpdateLinkGithub = (
|
||||
workflow: string,
|
||||
data: LinkGithubRepositoryRequest,
|
||||
params: RequestParams = {}
|
||||
) =>
|
||||
this.request<Workflow, APIErrors>({
|
||||
path: `/api/v1/workflows/${workflow}/link-github`,
|
||||
method: 'POST',
|
||||
body: data,
|
||||
secure: true,
|
||||
type: ContentType.Json,
|
||||
format: 'json',
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Create a pull request for a workflow
|
||||
*
|
||||
* @tags Workflow
|
||||
* @name StepRunUpdateCreatePr
|
||||
* @summary Create pull request
|
||||
* @request POST:/api/v1/step-runs/{step-run}/create-pr
|
||||
* @secure
|
||||
*/
|
||||
stepRunUpdateCreatePr = (
|
||||
stepRun: string,
|
||||
data: CreatePullRequestFromStepRun,
|
||||
params: RequestParams = {}
|
||||
) =>
|
||||
this.request<CreatePullRequestFromStepRun, APIErrors>({
|
||||
path: `/api/v1/step-runs/${stepRun}/create-pr`,
|
||||
method: 'POST',
|
||||
body: data,
|
||||
secure: true,
|
||||
type: ContentType.Json,
|
||||
format: 'json',
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description Get all workflow runs for a tenant
|
||||
*
|
||||
@@ -763,4 +874,60 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
|
||||
format: 'json',
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description List Github App installations
|
||||
*
|
||||
* @tags Github
|
||||
* @name GithubAppListInstallations
|
||||
* @summary List Github App installations
|
||||
* @request GET:/api/v1/github-app/installations
|
||||
* @secure
|
||||
*/
|
||||
githubAppListInstallations = (params: RequestParams = {}) =>
|
||||
this.request<ListGithubAppInstallationsResponse, APIErrors>({
|
||||
path: `/api/v1/github-app/installations`,
|
||||
method: 'GET',
|
||||
secure: true,
|
||||
format: 'json',
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description List Github App repositories
|
||||
*
|
||||
* @tags Github
|
||||
* @name GithubAppListRepos
|
||||
* @summary List Github App repositories
|
||||
* @request GET:/api/v1/github-app/installations/{gh-installation}/repos
|
||||
* @secure
|
||||
*/
|
||||
githubAppListRepos = (ghInstallation: string, params: RequestParams = {}) =>
|
||||
this.request<ListGithubReposResponse, APIErrors>({
|
||||
path: `/api/v1/github-app/installations/${ghInstallation}/repos`,
|
||||
method: 'GET',
|
||||
secure: true,
|
||||
format: 'json',
|
||||
...params,
|
||||
});
|
||||
/**
|
||||
* @description List Github App branches
|
||||
*
|
||||
* @tags Github
|
||||
* @name GithubAppListBranches
|
||||
* @summary List Github App branches
|
||||
* @request GET:/api/v1/github-app/installations/{gh-installation}/repos/{gh-repo-owner}/{gh-repo-name}/branches
|
||||
* @secure
|
||||
*/
|
||||
githubAppListBranches = (
|
||||
ghInstallation: string,
|
||||
ghRepoOwner: string,
|
||||
ghRepoName: string,
|
||||
params: RequestParams = {}
|
||||
) =>
|
||||
this.request<ListGithubBranchesResponse, APIErrors>({
|
||||
path: `/api/v1/github-app/installations/${ghInstallation}/repos/${ghRepoOwner}/${ghRepoName}/branches`,
|
||||
method: 'GET',
|
||||
secure: true,
|
||||
format: 'json',
|
||||
...params,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -320,6 +320,24 @@ export interface Workflow {
|
||||
lastRun?: WorkflowRun;
|
||||
/** The jobs of the workflow. */
|
||||
jobs?: Job[];
|
||||
deployment?: WorkflowDeploymentConfig;
|
||||
}
|
||||
|
||||
export interface WorkflowDeploymentConfig {
|
||||
metadata: APIResourceMeta;
|
||||
/** The repository name. */
|
||||
gitRepoName: string;
|
||||
/** The repository owner. */
|
||||
gitRepoOwner: string;
|
||||
/** The repository branch. */
|
||||
gitRepoBranch: string;
|
||||
/** The Github App installation. */
|
||||
githubAppInstallation?: GithubAppInstallation;
|
||||
/**
|
||||
* The id of the Github App installation.
|
||||
* @format uuid
|
||||
*/
|
||||
githubAppInstallationId: string;
|
||||
}
|
||||
|
||||
export interface WorkflowVersionMeta {
|
||||
@@ -579,3 +597,48 @@ export interface RerunStepRunRequest {
|
||||
export interface TriggerWorkflowRunRequest {
|
||||
input: object;
|
||||
}
|
||||
|
||||
export interface LinkGithubRepositoryRequest {
|
||||
/**
|
||||
* The repository name.
|
||||
* @minLength 36
|
||||
* @maxLength 36
|
||||
*/
|
||||
installationId: string;
|
||||
/** The repository name. */
|
||||
gitRepoName: string;
|
||||
/** The repository owner. */
|
||||
gitRepoOwner: string;
|
||||
/** The repository branch. */
|
||||
gitRepoBranch: string;
|
||||
}
|
||||
|
||||
export interface GithubBranch {
|
||||
branch_name: string;
|
||||
is_default: boolean;
|
||||
}
|
||||
|
||||
export interface GithubRepo {
|
||||
repo_owner: string;
|
||||
repo_name: string;
|
||||
}
|
||||
|
||||
export interface GithubAppInstallation {
|
||||
metadata: APIResourceMeta;
|
||||
installation_settings_url: string;
|
||||
account_name: string;
|
||||
account_avatar_url: string;
|
||||
}
|
||||
|
||||
export interface ListGithubAppInstallationsResponse {
|
||||
pagination: PaginationResponse;
|
||||
rows: GithubAppInstallation[];
|
||||
}
|
||||
|
||||
export type ListGithubReposResponse = GithubRepo[];
|
||||
|
||||
export type ListGithubBranchesResponse = GithubBranch[];
|
||||
|
||||
export interface CreatePullRequestFromStepRun {
|
||||
branchName: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user