diff --git a/.github/workflows/deploy-to-cloudrun-production.yml b/.github/workflows/deploy-to-cloudrun-production.yml new file mode 100644 index 000000000..49f3a1125 --- /dev/null +++ b/.github/workflows/deploy-to-cloudrun-production.yml @@ -0,0 +1,97 @@ +name: "Deploy to Production Cloud Run" + +on: + push: + branches: + - prod-gcp + - jp-testing + +env: + + # DB VARS + db_username: ${{ secrets.DB_USERNAME }} + db_password: ${{ secrets.DB_PASSWORD }} + db_hostname: ${{ secrets.DB_HOSTNAME }} + db_port: ${{ secrets.DB_PORT }} + db_name: ${{ secrets.DB_NAME }} + backend_api: ${{ secrets.BACKEND_API }} + tx_options: ${{ secrets.TX_OPTIONS }} + hotjar_site_id: ${{ secrets.HOTJAR_STAGE_ID }} + sentry_url: ${{ secrets.SENTRY_STAGE_URL }} + + # GCP VARS + BACKEND_DOCKER_IMAGE_URL: ${{ vars.BACKEND_GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} + FRONTEND_DOCKER_IMAGE_URL: ${{ vars.FRONTEND_GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} + GAR_LOCATION: ${{ vars.GCP_GAR_LOCATION }} + PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} + SERVICE_ACCOUNT: ${{ vars.GCP_SERVICE_ACCOUNT }} + WORKLOAD_IDENTITY_PROVIDER: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Google auth + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ env.SERVICE_ACCOUNT }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v1 + with: + project_id: ${{ env.PROJECT_ID }} + + - name: Build CAST Backend + run: |- + gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev + docker build -t ${{ env.BACKEND_DOCKER_IMAGE_URL }} --file backend/Dockerfile ./backend/ + docker push ${{ env.BACKEND_DOCKER_IMAGE_URL }} + + - name: Build CAST Frontend + env: + BACKEND: ${{ env.backend_api }} + run: | + gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev + cd frontend && sed -i -e 's/REPLACE_HOTJAR_SITE_ID/${{ env.hotjar_site_id }}/g' Dockerfile && sed -i -e 's|REPLACE_SENTRY_URL|${{ env.sentry_url }}|g' Dockerfile && docker build -f Dockerfile . -t ${{ env.FRONTEND_DOCKER_IMAGE_URL }} + docker push ${{ env.FRONTEND_DOCKER_IMAGE_URL }} + + deploy-production-backend: + needs: [build] + environment: production + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Google auth + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ vars.GCP_SERVICE_ACCOUNT }} + + - name: Deploy Backend to Cloud Run + uses: google-github-actions/deploy-cloudrun@v1 + with: + service: ${{ vars.BACKEND_GCP_SERVICE }} + image: ${{ env.BACKEND_DOCKER_IMAGE_URL }} + + - name: Deploy Frontend to Cloud Run + uses: google-github-actions/deploy-cloudrun@v1 + with: + service: ${{ vars.FRONTEND_GCP_SERVICE }} + image: ${{ env.FRONTEND_DOCKER_IMAGE_URL }} + diff --git a/.github/workflows/deploy-to-cloudrun-staging.yml b/.github/workflows/deploy-to-cloudrun-staging.yml new file mode 100644 index 000000000..70c331c1b --- /dev/null +++ b/.github/workflows/deploy-to-cloudrun-staging.yml @@ -0,0 +1,96 @@ +name: "Deploy to Staging Cloud Run" + +on: + push: + branches: + - staging + - jp-testing + +env: + + # DB VARS + db_username: ${{ secrets.DB_USERNAME }} + db_password: ${{ secrets.DB_PASSWORD }} + db_hostname: ${{ secrets.DB_HOSTNAME }} + db_port: ${{ secrets.DB_PORT }} + db_name: ${{ secrets.DB_NAME }} + backend_api: ${{ secrets.BACKEND_API }} + tx_options: ${{ secrets.TX_OPTIONS }} + hotjar_site_id: ${{ secrets.HOTJAR_STAGE_ID }} + sentry_url: ${{ secrets.SENTRY_STAGE_URL }} + + # GCP VARS + BACKEND_DOCKER_IMAGE_URL: ${{ vars.BACKEND_GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} + FRONTEND_DOCKER_IMAGE_URL: ${{ vars.FRONTEND_GCP_DOCKER_IMAGE_URL }}:${{ github.sha }} + GAR_LOCATION: ${{ vars.GCP_GAR_LOCATION }} + PROJECT_ID: ${{ vars.GCP_PROJECT_ID }} + SERVICE_ACCOUNT: ${{ vars.GCP_SERVICE_ACCOUNT }} + WORKLOAD_IDENTITY_PROVIDER: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Google auth + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ env.WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ env.SERVICE_ACCOUNT }} + + - name: Set up Cloud SDK + uses: google-github-actions/setup-gcloud@v1 + with: + project_id: ${{ env.PROJECT_ID }} + + - name: Docker Auth + run: |- + gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev + docker build -t ${{ env.BACKEND_DOCKER_IMAGE_URL }} --file backend/Dockerfile ./backend/ + docker push ${{ env.BACKEND_DOCKER_IMAGE_URL }} + + - name: build and push frontend - stage - + env: + BACKEND: ${{ env.backend_api }} + run: | + gcloud auth configure-docker ${{ env.GAR_LOCATION }}-docker.pkg.dev + cd frontend && sed -i -e 's/REPLACE_HOTJAR_SITE_ID/${{ env.hotjar_site_id }}/g' Dockerfile.stage && sed -i -e 's|REPLACE_SENTRY_URL|${{ env.sentry_url }}|g' Dockerfile.stage && docker build -f Dockerfile.stage . -t ${{ env.FRONTEND_DOCKER_IMAGE_URL }} + + deploy-staging-backend: + needs: [build] + environment: staging + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Google auth + id: auth + uses: google-github-actions/auth@v2 + with: + token_format: 'access_token' + workload_identity_provider: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }} + service_account: ${{ vars.GCP_SERVICE_ACCOUNT }} + + - name: Deploy Backend to Cloud Run + uses: google-github-actions/deploy-cloudrun@v1 + with: + service: ${{ vars.BACKEND_GCP_SERVICE }} + image: ${{ env.BACKEND_DOCKER_IMAGE_URL }} + + - name: Deploy Frontend to Cloud Run + uses: google-github-actions/deploy-cloudrun@v1 + with: + service: ${{ vars.FRONTEND_GCP_SERVICE }} + image: ${{ env.FRONTEND_DOCKER_IMAGE_URL }} + diff --git a/backend/main/server/app.go b/backend/main/server/app.go index 43f377180..b8cac3701 100644 --- a/backend/main/server/app.go +++ b/backend/main/server/app.go @@ -1,42 +1,43 @@ package server import ( - // "errors" - "context" "encoding/json" "flag" "fmt" "io/ioutil" "net/http" + "net/url" "os" "strings" - "github.com/DapperCollectives/CAST/backend/main/middleware" - "github.com/DapperCollectives/CAST/backend/main/models" - "github.com/DapperCollectives/CAST/backend/main/shared" - "github.com/DapperCollectives/CAST/backend/main/strategies" "github.com/axiomzen/envconfig" "github.com/gorilla/mux" "github.com/jackc/pgx/v4/pgxpool" - "github.com/rs/zerolog" "github.com/rs/zerolog/log" + + "github.com/DapperCollectives/CAST/backend/main/middleware" + "github.com/DapperCollectives/CAST/backend/main/models" + "github.com/DapperCollectives/CAST/backend/main/shared" + "github.com/DapperCollectives/CAST/backend/main/strategies" ) -type Database shared.Database -type IpfsClient shared.IpfsClient -type Allowlist shared.Allowlist -type Vote models.Vote -type Proposal models.Proposal -type Community models.Community -type Balance models.Balance -type List models.List -type ListRequest models.ListPayload -type ListUpdatePayload models.ListUpdatePayload -type CommunityUser models.CommunityUser -type CommunityUserPayload models.CommunityUserPayload -type UserCommunity models.UserCommunity +type ( + Database shared.Database + IpfsClient shared.IpfsClient + Allowlist shared.Allowlist + Vote models.Vote + Proposal models.Proposal + Community models.Community + Balance models.Balance + List models.List + ListRequest models.ListPayload + ListUpdatePayload models.ListUpdatePayload + CommunityUser models.CommunityUser + CommunityUserPayload models.CommunityUserPayload + UserCommunity models.UserCommunity +) type TxOptionsAddresses []string @@ -54,9 +55,19 @@ type App struct { } type Strategy interface { - TallyVotes(votes []*models.VoteWithBalance, p *models.ProposalResults, proposal *models.Proposal) (models.ProposalResults, error) - GetVotes(votes []*models.VoteWithBalance, proposal *models.Proposal) ([]*models.VoteWithBalance, error) - GetVoteWeightForBalance(vote *models.VoteWithBalance, proposal *models.Proposal) (float64, error) + TallyVotes( + votes []*models.VoteWithBalance, + p *models.ProposalResults, + proposal *models.Proposal, + ) (models.ProposalResults, error) + GetVotes( + votes []*models.VoteWithBalance, + proposal *models.Proposal, + ) ([]*models.VoteWithBalance, error) + GetVoteWeightForBalance( + vote *models.VoteWithBalance, + proposal *models.Proposal, + ) (float64, error) InitStrategy(f *shared.FlowAdapter, db *shared.Database) FetchBalance(b *models.Balance, p *models.Proposal) (*models.Balance, error) RequiresSnapshot() bool @@ -164,7 +175,7 @@ func (a *App) Initialize() { } // Create Map for Flow Adaptor to look up when voting - var customScriptsMap = make(map[string]shared.CustomScript) + customScriptsMap := make(map[string]shared.CustomScript) for _, script := range customScripts { customScriptsMap[script.Key] = script } @@ -202,8 +213,7 @@ func (a *App) ConnectDB(username, password, host, port, dbname string) { database.Context = context.Background() database.Name = dbname - connectionString := - fmt.Sprintf("postgres://%s:%s@%s:%s/%s", username, password, host, port, dbname) + connectionString := ToUnixURL(false, username, password, dbname, host) pconf, confErr := pgxpool.ParseConfig(connectionString) if confErr != nil { @@ -226,3 +236,30 @@ func (a *App) ConnectDB(username, password, host, port, dbname string) { log.Info().Msgf("Successfully created Postgres conn pool") } } + +func ToUnixURL(ssl bool, username, password, db, host string) string { + urlStr := "postgresql://" + + if len(username) == 0 { + username = "postgres" + } + urlStr += username + + if len(password) > 0 { + urlStr = urlStr + ":" + url.PathEscape(password) + } + urlStr += "@" + + urlStr += "/" + url.PathEscape(db) + + // Append query parameters + urlStr += "?" + "host=" + host + + mode := "" + if !ssl { + mode = "&sslmode=disable" + } + + urlStr += mode + return urlStr +} diff --git a/frontend/Dockerfile b/frontend/Dockerfile index ca9f28d14..baeca5438 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -15,7 +15,7 @@ RUN yarn install ENV NODE_ENV "production" ENV REACT_APP_APP_ENV "production" ENV REACT_APP_FRONTEND_URL "cast.fyi" -ENV REACT_APP_BACK_END_SERVER_API "https://api.cast.fyi" +ENV REACT_APP_BACK_END_SERVER_API "https://cast-backend-prod-cxc3jxjjdq-uc.a.run.app" ENV REACT_APP_IPFS_GATEWAY "https://dappercollectives.mypinata.cloud/ipfs" ENV REACT_APP_FLOW_ENV "mainnet" ENV REACT_APP_TX_OPTIONS_ADDRS "0xe0de919ed4ebeee4,0x7f81b82fa0e59b17"