-
Notifications
You must be signed in to change notification settings - Fork 567
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1723 from diggerhq/feat-drift-app
feat drift app
- Loading branch information
Showing
35 changed files
with
5,485 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
name: driftapp Deploy | ||
on: | ||
push: | ||
branches: | ||
- develop # change to main if needed | ||
- feat-drift-app | ||
jobs: | ||
deploy: | ||
name: Deploy app | ||
runs-on: ubuntu-latest | ||
concurrency: deploy-group # optional: ensure only one action runs at a time | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: superfly/flyctl-actions/setup-flyctl@master | ||
- run: flyctl deploy --remote-only --config fly-drift.toml | ||
env: | ||
FLY_API_TOKEN: ${{ secrets.FLYIO_DRIFT_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
FROM golang:1.22 as builder | ||
ARG COMMIT_SHA | ||
RUN echo "commit sha: ${COMMIT_SHA}" | ||
|
||
# Set the working directory | ||
WORKDIR $GOPATH/src/github.com/diggerhq/digger | ||
|
||
# Copy all required source, blacklist files that are not required through `.dockerignore` | ||
COPY . . | ||
|
||
# Get the vendor library | ||
RUN go version | ||
|
||
# RUN vgo install | ||
|
||
# https://github.com/ethereum/go-ethereum/issues/2738 | ||
# Build static binary "-getmode=vendor" does not work with go-ethereum | ||
|
||
RUN go build -ldflags="-X 'main.Version=${COMMIT_SHA}'" -o drift_exe ./ee/drift/ | ||
|
||
# Multi-stage build will just copy the binary to an alpine image. | ||
FROM ubuntu:24.04 as runner | ||
ENV ATLAS_VERSION v0.16.0 | ||
ARG COMMIT_SHA | ||
WORKDIR /app | ||
|
||
RUN apt-get update && apt-get install -y ca-certificates curl && apt-get install -y git && apt-get clean all | ||
RUN update-ca-certificates | ||
|
||
RUN echo "commit sha: ${COMMIT_SHA}" | ||
|
||
# install atlas | ||
RUN curl -sSf https://atlasgo.sh | sh | ||
|
||
|
||
|
||
# Set gin to production | ||
#ENV GIN_MODE=release | ||
|
||
# Expose the running port | ||
EXPOSE 3000 | ||
|
||
# Copy the binary to the corresponding folder | ||
COPY --from=builder /go/src/github.com/diggerhq/digger/drift_exe /app/driftapp | ||
COPY --from=builder /go/src/github.com/diggerhq/digger/ee/drift/scripts/entrypoint.sh /app/entrypoint.sh | ||
ADD ee/backend/templates ./templates | ||
|
||
# Run the binary | ||
ENTRYPOINT ["/bin/bash", "/app/entrypoint.sh"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# flyctl launch added from .gitignore | ||
**/.env | ||
fly.toml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.env | ||
main | ||
drift |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
FROM golang:1.22 as builder | ||
ARG COMMIT_SHA | ||
RUN echo "commit sha: ${COMMIT_SHA}" | ||
|
||
# Set the working directory | ||
WORKDIR $GOPATH/src/github.com/diggerhq/drift | ||
|
||
# Copy all required source, blacklist files that are not required through `.dockerignore` | ||
COPY . . | ||
|
||
# Get the vendor library | ||
RUN go version | ||
|
||
# RUN vgo install | ||
|
||
# https://github.com/ethereum/go-ethereum/issues/2738 | ||
# Build static binary "-getmode=vendor" does not work with go-ethereum | ||
|
||
RUN go build -ldflags="-X 'main.Version=${COMMIT_SHA}'" -o driftapp_exe ./ | ||
|
||
# Multi-stage build will just copy the binary to an alpine image. | ||
FROM ubuntu:24.04 as runner | ||
ENV ATLAS_VERSION v0.16.0 | ||
ARG COMMIT_SHA | ||
WORKDIR /app | ||
|
||
RUN apt-get update && apt-get install -y ca-certificates curl && apt-get install -y git && apt-get clean all | ||
RUN update-ca-certificates | ||
|
||
RUN echo "commit sha: ${COMMIT_SHA}" | ||
|
||
|
||
# Set gin to production | ||
#ENV GIN_MODE=release | ||
|
||
# Expose the running port | ||
EXPOSE 3000 | ||
|
||
# Copy the binary to the corresponding folder | ||
COPY --from=builder /go/src/github.com/diggerhq/drift/driftapp_exe /app/driftapp | ||
COPY --from=builder /go/src/github.com/diggerhq/drift/scripts/entrypoint.sh /app/entrypoint.sh | ||
|
||
# Run the binary | ||
ENTRYPOINT ["/bin/bash", "/app/entrypoint.sh"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package controllers | ||
|
||
import "github.com/diggerhq/digger/next/utils" | ||
|
||
type MainController struct { | ||
GithubClientProvider utils.GithubClientProvider | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
package controllers | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"github.com/diggerhq/digger/ee/drift/dbmodels" | ||
"github.com/diggerhq/digger/ee/drift/middleware" | ||
"github.com/diggerhq/digger/ee/drift/model" | ||
next_utils "github.com/diggerhq/digger/next/utils" | ||
"github.com/gin-gonic/gin" | ||
"github.com/google/go-github/v61/github" | ||
"golang.org/x/oauth2" | ||
"log" | ||
"net/http" | ||
"os" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
func (mc MainController) GithubAppCallbackPage(c *gin.Context) { | ||
installationId := c.Request.URL.Query()["installation_id"][0] | ||
//setupAction := c.Request.URL.Query()["setup_action"][0] | ||
code := c.Request.URL.Query()["code"][0] | ||
clientId := os.Getenv("GITHUB_APP_CLIENT_ID") | ||
clientSecret := os.Getenv("GITHUB_APP_CLIENT_SECRET") | ||
|
||
installationId64, err := strconv.ParseInt(installationId, 10, 64) | ||
if err != nil { | ||
log.Printf("err: %v", err) | ||
c.String(http.StatusInternalServerError, "Failed to parse installation_id.") | ||
return | ||
} | ||
|
||
result, installation, err := validateGithubCallback(mc.GithubClientProvider, clientId, clientSecret, code, installationId64) | ||
if !result { | ||
log.Printf("Failed to validated installation id, %v\n", err) | ||
c.String(http.StatusInternalServerError, "Failed to validate installation_id.") | ||
return | ||
} | ||
|
||
// retrive org for current orgID | ||
orgId, exists := c.Get(middleware.ORGANISATION_ID_KEY) | ||
if !exists { | ||
log.Printf("missing argument orgId in github callback") | ||
c.String(http.StatusBadRequest, "missing orgID in request") | ||
return | ||
} | ||
org, err := dbmodels.DB.GetOrganisationById(orgId) | ||
if err != nil { | ||
log.Printf("Error fetching organisation: %v", err) | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error fetching organisation"}) | ||
return | ||
} | ||
|
||
// create a github installation link (org ID matched to installation ID) | ||
_, err = dbmodels.DB.CreateGithubInstallationLink(org.ID, installationId) | ||
if err != nil { | ||
log.Printf("Error saving GithubInstallationLink to database: %v", err) | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error updating GitHub installation"}) | ||
return | ||
} | ||
|
||
client, _, err := mc.GithubClientProvider.Get(*installation.AppID, installationId64) | ||
if err != nil { | ||
log.Printf("Error retriving github client: %v", err) | ||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error fetching organisation"}) | ||
return | ||
|
||
} | ||
|
||
// we get repos accessible to this installation | ||
listRepos, _, err := client.Apps.ListRepos(context.Background(), nil) | ||
if err != nil { | ||
log.Printf("Failed to validated list existing repos, %v\n", err) | ||
c.String(http.StatusInternalServerError, "Failed to list existing repos: %v", err) | ||
return | ||
} | ||
repos := listRepos.Repositories | ||
|
||
// reset all existing repos (soft delete) | ||
var ExistingRepos []model.Repo | ||
err = dbmodels.DB.GormDB.Delete(ExistingRepos, "organization_id=?", orgId).Error | ||
if err != nil { | ||
log.Printf("could not delete repos: %v", err) | ||
c.String(http.StatusInternalServerError, "could not delete repos: %v", err) | ||
return | ||
} | ||
|
||
// here we mark repos that are available one by one | ||
for _, repo := range repos { | ||
repoFullName := *repo.FullName | ||
repoOwner := strings.Split(*repo.FullName, "/")[0] | ||
repoName := *repo.Name | ||
repoUrl := fmt.Sprintf("https://github.com/%v", repoFullName) | ||
|
||
_, _, err = dbmodels.CreateOrGetDiggerRepoForGithubRepo(repoFullName, repoOwner, repoName, repoUrl, installationId64, *installation.AppID, *installation.Account.ID, *installation.Account.Login) | ||
if err != nil { | ||
log.Printf("createOrGetDiggerRepoForGithubRepo error: %v", err) | ||
c.String(http.StatusInternalServerError, "createOrGetDiggerRepoForGithubRepo error: %v", err) | ||
return | ||
} | ||
} | ||
|
||
c.HTML(http.StatusOK, "github_success.tmpl", gin.H{}) | ||
} | ||
|
||
// why this validation is needed: https://roadie.io/blog/avoid-leaking-github-org-data/ | ||
// validation based on https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-a-user-access-token-for-a-github-app , step 3 | ||
func validateGithubCallback(githubClientProvider next_utils.GithubClientProvider, clientId string, clientSecret string, code string, installationId int64) (bool, *github.Installation, error) { | ||
ctx := context.Background() | ||
type OAuthAccessResponse struct { | ||
AccessToken string `json:"access_token"` | ||
} | ||
httpClient := http.Client{} | ||
|
||
githubHostname := "github.com" | ||
reqURL := fmt.Sprintf("https://%v/login/oauth/access_token?client_id=%s&client_secret=%s&code=%s", githubHostname, clientId, clientSecret, code) | ||
req, err := http.NewRequest(http.MethodPost, reqURL, nil) | ||
if err != nil { | ||
return false, nil, fmt.Errorf("could not create HTTP request: %v\n", err) | ||
} | ||
req.Header.Set("accept", "application/json") | ||
|
||
res, err := httpClient.Do(req) | ||
if err != nil { | ||
return false, nil, fmt.Errorf("request to login/oauth/access_token failed: %v\n", err) | ||
} | ||
|
||
if err != nil { | ||
return false, nil, fmt.Errorf("Failed to read response's body: %v\n", err) | ||
} | ||
|
||
var t OAuthAccessResponse | ||
if err := json.NewDecoder(res.Body).Decode(&t); err != nil { | ||
return false, nil, fmt.Errorf("could not parse JSON response: %v\n", err) | ||
} | ||
|
||
ts := oauth2.StaticTokenSource( | ||
&oauth2.Token{AccessToken: t.AccessToken}, | ||
) | ||
tc := oauth2.NewClient(ctx, ts) | ||
//tc := &http.Client{ | ||
// Transport: &oauth2.Transport{ | ||
// Base: httpClient.Transport, | ||
// Source: oauth2.ReuseTokenSource(nil, ts), | ||
// }, | ||
//} | ||
|
||
client, err := githubClientProvider.NewClient(tc) | ||
if err != nil { | ||
log.Printf("could create github client: %v", err) | ||
return false, nil, fmt.Errorf("could not create github client: %v", err) | ||
} | ||
|
||
installationIdMatch := false | ||
// list all installations for the user | ||
var matchedInstallation *github.Installation | ||
installations, _, err := client.Apps.ListUserInstallations(ctx, nil) | ||
if err != nil { | ||
log.Printf("could not retrieve installations: %v", err) | ||
return false, nil, fmt.Errorf("could not retrieve installations: %v", installationId) | ||
} | ||
log.Printf("installations %v", installations) | ||
for _, v := range installations { | ||
log.Printf("installation id: %v\n", *v.ID) | ||
if *v.ID == installationId { | ||
matchedInstallation = v | ||
installationIdMatch = true | ||
} | ||
} | ||
if !installationIdMatch { | ||
return false, nil, fmt.Errorf("InstallationId %v doesn't match any id for specified user\n", installationId) | ||
} | ||
|
||
return true, matchedInstallation, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package controllers | ||
|
||
import ( | ||
"github.com/gin-gonic/gin" | ||
) | ||
|
||
func (mc MainController) Ping(c *gin.Context) { | ||
c.String(200, "pong") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
package main | ||
|
||
import ( | ||
"os" | ||
|
||
"gorm.io/driver/postgres" | ||
"gorm.io/gen" | ||
"gorm.io/gorm" | ||
) | ||
|
||
// Dynamic SQL | ||
type Querier interface { | ||
// SELECT * FROM @@table WHERE name = @name{{if role !=""}} AND role = @role{{end}} | ||
FilterWithNameAndRole(name, role string) ([]gen.T, error) | ||
} | ||
|
||
func main() { | ||
g := gen.NewGenerator(gen.Config{ | ||
OutPath: "../models_generated", | ||
Mode: gen.WithoutContext | gen.WithDefaultQuery | gen.WithQueryInterface, // generate mode | ||
}) | ||
|
||
dburl := os.Getenv("DB_URL") | ||
if dburl == "" { | ||
dburl = "postgresql://postgres:[email protected]:54322/postgres" | ||
} | ||
gormdb, _ := gorm.Open(postgres.Open(dburl)) | ||
g.UseDB(gormdb) // reuse your gorm db | ||
|
||
g.ApplyBasic( | ||
// Generate structs from all tables of current database | ||
g.GenerateAllTable()..., | ||
) | ||
|
||
// Generate the code | ||
g.Execute() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
module dbgen | ||
|
||
go 1.22.0 | ||
|
||
require ( | ||
gorm.io/driver/postgres v1.5.9 | ||
gorm.io/gen v0.3.26 | ||
gorm.io/gorm v1.25.12 | ||
) | ||
|
||
require ( | ||
github.com/go-sql-driver/mysql v1.7.0 // indirect | ||
github.com/jackc/pgpassfile v1.0.0 // indirect | ||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect | ||
github.com/jackc/pgx/v5 v5.5.5 // indirect | ||
github.com/jackc/puddle/v2 v2.2.1 // indirect | ||
github.com/jinzhu/inflection v1.0.0 // indirect | ||
github.com/jinzhu/now v1.1.5 // indirect | ||
golang.org/x/crypto v0.17.0 // indirect | ||
golang.org/x/mod v0.17.0 // indirect | ||
golang.org/x/sync v0.8.0 // indirect | ||
golang.org/x/text v0.18.0 // indirect | ||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect | ||
gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c // indirect | ||
gorm.io/driver/mysql v1.5.7 // indirect | ||
gorm.io/hints v1.1.0 // indirect | ||
gorm.io/plugin/dbresolver v1.5.3 // indirect | ||
) |
Oops, something went wrong.