-
Notifications
You must be signed in to change notification settings - Fork 5
Sprint 18 #596
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Sprint 18 #596
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
a50addb
Feat/support licensee transaction fees (#525)
landonshumway-ia 5df35f5
Frontend/staff user reinvite and delete (#531)
jsandoval81 661e84f
Feat/tie purchase flow (#534)
ChiefStief 1031f65
Feat/get one ssn (#535)
jusdino a4484e1
Frontend/staff user status (#557)
jsandoval81 bce57fa
AWS resource script (#538)
jlkravitz 56a1738
Bug/fix csp provider (#546)
ChiefStief 40212cb
Feat/ display transaction fees (#559)
ChiefStief 7d56f60
Feat/reduced ssn api (#541)
jusdino d8afcc9
Rename compactName field to compactAbbr (#558)
landonshumway-ia bfcd193
Feat/display license type (#562)
ChiefStief 38301b4
Feat/reporting updates (#542)
landonshumway-ia a8ed079
Feat/deactivate privilege (#563)
jusdino 686fa0c
Add license field descriptions table (#582)
jusdino 28e8479
Frontend/deactivate privilege (#578)
jsandoval81 092b166
Add dependency license report script (#568)
jusdino de6100f
Frontend/prod deploy pipeline (#591)
jsandoval81 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,158 @@ | ||
# Compact Connect - Web Frontend Deployment - Production | ||
|
||
name: Webroot-Deploy-Production | ||
|
||
jlkravitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# Controls when the action will run. | ||
on: | ||
# Triggers the workflow on pushes to trunk branches involving changes to web frontend files | ||
jlkravitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
push: | ||
branches: | ||
- main | ||
jlkravitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
- frontend/prod-deploy-pipeline | ||
paths: | ||
- webroot/** | ||
|
||
# Allows you to run this workflow manually from the Actions tab | ||
jlkravitz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
workflow_dispatch: | ||
|
||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel | ||
jobs: | ||
WebrootDeploy: | ||
# Only run this workflow in certain repos | ||
if: github.repository == 'csg-org/CompactConnect' | ||
|
||
# Runner OS | ||
runs-on: ubuntu-latest | ||
|
||
# Job needs id-token access to work with GitHub OIDC to AWS IAM Role | ||
permissions: | ||
id-token: write | ||
contents: read | ||
|
||
# Define environment-specific values | ||
env: | ||
ENVIRONMENT_NAME: Production Frontend | ||
AWS_REGION: us-east-1 | ||
AWS_ROLE: ${{ secrets.PROD_WEBROOT_AWS_ROLE }} | ||
AWS_ROLE_SESSION: WebrootDeployProduction | ||
AWS_S3_BUCKET: ${{ secrets.PROD_WEBROOT_AWS_S3_BUCKET }} | ||
AWS_CLOUDFRONT_DISTRIBUTION: ${{ secrets.PROD_WEBROOT_AWS_CLOUDFRONT_DISTRIBUTION }} | ||
SLACK_BOT_TOKEN: ${{ secrets.IA_SLACK_BOT_TOKEN }} | ||
BASE_URL: / | ||
VUE_APP_DOMAIN: https://app.compactconnect.org | ||
VUE_APP_ROBOTS_META: index,follow | ||
VUE_APP_API_STATE_ROOT: https://api.compactconnect.org | ||
VUE_APP_API_LICENSE_ROOT: https://api.compactconnect.org | ||
VUE_APP_API_USER_ROOT: https://api.compactconnect.org | ||
VUE_APP_COGNITO_REGION: us-east-1 | ||
VUE_APP_COGNITO_AUTH_DOMAIN_STAFF: https://compact-connect-staff.auth.us-east-1.amazoncognito.com | ||
VUE_APP_COGNITO_CLIENT_ID_STAFF: ${{ secrets.PROD_WEBROOT_COGNITO_CLIENT_ID_STAFF }} | ||
VUE_APP_COGNITO_AUTH_DOMAIN_LICENSEE: https://compact-connect-provider.auth.us-east-1.amazoncognito.com | ||
VUE_APP_COGNITO_CLIENT_ID_LICENSEE: ${{ secrets.PROD_WEBROOT_COGNITO_CLIENT_ID_LICENSEE }} | ||
VUE_APP_RECAPTCHA_KEY: 6LcEQckqAAAAAJUQDEO1KsoeH17-EH5h2UfrwdyK | ||
|
||
steps: | ||
- run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." | ||
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" | ||
- run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." | ||
|
||
# Set AWS CLI credentials | ||
- name: Configure AWS Credentials | ||
uses: aws-actions/configure-aws-credentials@v4 | ||
with: | ||
aws-region: ${{ env.AWS_REGION }} | ||
role-to-assume: ${{ env.AWS_ROLE }} | ||
role-session-name: ${{ env.AWS_ROLE_SESSION }} | ||
|
||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it | ||
- uses: actions/checkout@v2 | ||
|
||
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." | ||
- run: echo "🖥️ The workflow is now ready to test your code on the runner." | ||
|
||
# Setup Node | ||
- name: Setup Node | ||
uses: actions/setup-node@v1 | ||
with: | ||
node-version: '22.1.0' | ||
|
||
# Use any cached yarn dependencies (saves build time) | ||
- uses: actions/cache@v4 | ||
with: | ||
path: '**/node_modules' | ||
key: ${{ runner.os }}-modules-${{ hashFiles('**/yarn.lock') }} | ||
|
||
# Install Yarn Dependencies | ||
- name: Install JS dependencies | ||
run: yarn install --ignore-engines | ||
working-directory: ./webroot | ||
|
||
# Run Linter Checks | ||
- name: Run linter | ||
run: yarn lint --no-fix | ||
working-directory: ./webroot | ||
|
||
# Build app | ||
- name: Build Vue app | ||
env: | ||
NODE_ENV: production | ||
BASE_URL: ${{ env.BASE_URL }} | ||
VUE_APP_DOMAIN: ${{ env.VUE_APP_DOMAIN }} | ||
VUE_APP_ROBOTS_META: ${{ env.VUE_APP_ROBOTS_META }} | ||
VUE_APP_API_STATE_ROOT: ${{ env.VUE_APP_API_STATE_ROOT }} | ||
VUE_APP_API_LICENSE_ROOT: ${{ env.VUE_APP_API_LICENSE_ROOT }} | ||
VUE_APP_API_USER_ROOT: ${{ env.VUE_APP_API_USER_ROOT }} | ||
VUE_APP_COGNITO_REGION: ${{ env.VUE_APP_COGNITO_REGION }} | ||
VUE_APP_COGNITO_AUTH_DOMAIN_STAFF: ${{ env.VUE_APP_COGNITO_AUTH_DOMAIN_STAFF }} | ||
VUE_APP_COGNITO_CLIENT_ID_STAFF: ${{ env.VUE_APP_COGNITO_CLIENT_ID_STAFF }} | ||
VUE_APP_COGNITO_AUTH_DOMAIN_LICENSEE: ${{ env.VUE_APP_COGNITO_AUTH_DOMAIN_LICENSEE }} | ||
VUE_APP_COGNITO_CLIENT_ID_LICENSEE: ${{ env.VUE_APP_COGNITO_CLIENT_ID_LICENSEE }} | ||
VUE_APP_RECAPTCHA_KEY: ${{ env.VUE_APP_RECAPTCHA_KEY }} | ||
run: yarn build | ||
working-directory: ./webroot | ||
|
||
# Clear out S3 bucket | ||
- name: Clear S3 bucket | ||
run: aws s3 rm ${{ env.AWS_S3_BUCKET }} --recursive | ||
working-directory: ./webroot | ||
|
||
# Upload build directory to S3 | ||
- name: Upload files to S3 | ||
run: aws s3 cp dist ${{ env.AWS_S3_BUCKET }} --recursive | ||
working-directory: ./webroot | ||
|
||
# Initiate Cloudfront invalidation | ||
- name: Invalidate cache on Cloudfront distribution | ||
run: > | ||
CLOUDFRONT_INVALIDATION_ID=$(aws cloudfront create-invalidation | ||
--distribution-id ${{ env.AWS_CLOUDFRONT_DISTRIBUTION }} | ||
--paths "/" | ||
--query Invalidation.Id | ||
--output text) | ||
&& echo "CLOUDFRONT_INVALIDATION_ID=$CLOUDFRONT_INVALIDATION_ID" >> $GITHUB_ENV | ||
|
||
# Wait for Cloudfront invalidation to complete | ||
- name: Wait for Cloudfront invalidation | ||
run: aws cloudfront wait invalidation-completed --distribution-id ${{ env.AWS_CLOUDFRONT_DISTRIBUTION }} --id ${{ env.CLOUDFRONT_INVALIDATION_ID }} | ||
|
||
# Notify to Slack | ||
- name: Post to a Slack channel | ||
uses: slackapi/[email protected] | ||
# https://github.com/slackapi/slack-github-action?tab=readme-ov-file#technique-2-slack-app | ||
with: | ||
channel-id: 'z_jcc_and_inspiringapps' | ||
# https://app.slack.com/block-kit-builder | ||
payload: | | ||
{ | ||
"blocks": [ | ||
{ | ||
"type": "section", | ||
"text": { | ||
"type": "mrkdwn", | ||
"text": "CompactConnect deployment:\n\n*<${{ env.VUE_APP_DOMAIN }}|${{ env.ENVIRONMENT_NAME }} environment>* :rocket:" | ||
} | ||
} | ||
] | ||
} | ||
env: | ||
SLACK_BOT_TOKEN: ${{ env.SLACK_BOT_TOKEN }} |
This file contains hidden or 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
This file contains hidden or 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,184 @@ | ||
#!/usr/bin/env python3 | ||
# ruff: noqa: T201 we use print statements for local scripts | ||
"""Script to fetch AWS resources names and IDs required for local deployment. Run from `backend/compact-connect`. | ||
|
||
To display in human-readable format: | ||
python fetch_aws_resources.py | ||
|
||
To output in .env format: | ||
python fetch_aws_resources.py --as-env | ||
|
||
The CLI must also be configured with AWS credentials that have appropriate access to Cognito and DynamoDB | ||
""" | ||
|
||
import argparse | ||
|
||
import boto3.session | ||
|
||
# Initialize AWS clients | ||
cognito_client = boto3.client('cognito-idp') | ||
cloudformation_client = boto3.client('cloudformation') | ||
|
||
# Fetch the AWS region | ||
aws_region = boto3.session.Session().region_name | ||
|
||
# List of stack names | ||
STACK_NAMES = [ | ||
'Sandbox-TransactionMonitoringStack', | ||
'Sandbox-APIStack', | ||
'Sandbox-UIStack', | ||
'Sandbox-IngestStack', | ||
'Sandbox-PersistentStack', | ||
] | ||
|
||
|
||
def get_stack_outputs(stack_name): | ||
"""Fetch outputs from CloudFormation stack""" | ||
try: | ||
response = cloudformation_client.describe_stacks(StackName=stack_name) | ||
stack = response['Stacks'][0] | ||
return {output['OutputKey']: output['OutputValue'] for output in stack.get('Outputs', [])} | ||
except Exception as e: # noqa: BLE001 | ||
print(f'Error retrieving stack {stack_name}: {e}') | ||
return {} | ||
|
||
|
||
def get_cognito_details(user_pool_id): | ||
"""Fetch Cognito User Pool Name and Client ID""" | ||
try: | ||
pool_response = cognito_client.describe_user_pool(UserPoolId=user_pool_id) | ||
user_pool_name = pool_response['UserPool']['Name'] | ||
|
||
client_response = cognito_client.list_user_pool_clients(UserPoolId=user_pool_id) | ||
client_id = ( | ||
client_response['UserPoolClients'][0]['ClientId'] | ||
if client_response['UserPoolClients'] | ||
else 'No Client ID Found' | ||
) | ||
|
||
return user_pool_name, client_id | ||
except Exception as e: # noqa: BLE001 | ||
print(f'Error retrieving Cognito details for {user_pool_id}: {e}') | ||
return 'Unknown', 'Unknown' | ||
|
||
|
||
def get_cognito_login_url(user_pool_domain): | ||
"""Construct Cognito Hosted UI Login URL""" | ||
if user_pool_domain: | ||
return f'https://{user_pool_domain}.auth.{aws_region}.amazoncognito.com/login' | ||
return None | ||
|
||
|
||
def extract_table_name(value): | ||
"""Extracts the actual DynamoDB table name from an ARN""" | ||
if value.startswith('arn:aws:dynamodb:'): | ||
return value.split(':')[-1].split('/')[-1] | ||
return value | ||
|
||
|
||
def fetch_resources(): | ||
"""Fetch all required AWS resources""" | ||
api_gateway_url = None | ||
provider_details = {} | ||
staff_details = {} | ||
|
||
for stack in STACK_NAMES: | ||
outputs = get_stack_outputs(stack) | ||
|
||
for key, value in outputs.items(): | ||
# API Gateway Endpoint | ||
if 'ApiGateway' in key or 'Endpoint' in key: | ||
if value.startswith('https://') and 'execute-api' in value: | ||
api_gateway_url = value | ||
|
||
# Provider Users (Cognito + DynamoDB) | ||
if 'ProviderUsers' in key: | ||
if 'UserPoolId' in key: | ||
provider_details['user_pool_id'] = value | ||
provider_details['user_pool_name'], provider_details['client_id'] = get_cognito_details(value) | ||
elif 'UsersDomain' in key: | ||
provider_details['login_url'] = get_cognito_login_url(value) | ||
|
||
# Staff Users (Cognito + DynamoDB) | ||
if 'StaffUsersGreen' in key: | ||
if 'UserPoolId' in key: | ||
staff_details['user_pool_id'] = value | ||
staff_details['user_pool_name'], staff_details['client_id'] = get_cognito_details(value) | ||
elif 'UsersDomain' in key: | ||
staff_details['login_url'] = get_cognito_login_url(value) | ||
|
||
# Find associated DynamoDB tables | ||
if 'Table' in key: | ||
if 'ProviderTable' in value: | ||
provider_details['dynamodb_table'] = extract_table_name(value) | ||
if 'StaffUsersGreen' in value: | ||
staff_details['dynamodb_table'] = extract_table_name(value) | ||
|
||
return api_gateway_url, provider_details, staff_details | ||
|
||
|
||
def print_human_readable(api_gateway_url, provider_details, staff_details): | ||
"""Prints data in a human-readable format""" | ||
print('\n\033[1;34m=== AWS Resource Information ===\033[0m\n') # Blue header | ||
|
||
# Print API Gateway URL | ||
if api_gateway_url: | ||
print(f'\033[1;32mAPI Gateway Endpoint:\033[0m {api_gateway_url}\n') # Green header | ||
|
||
# Print Provider User Pool Details | ||
print('\033[1;36m=== Provider Users ===\033[0m') # Cyan header | ||
if provider_details: | ||
print(f'\033[1mLogin URL:\033[0m {provider_details.get("login_url", "N/A")}') | ||
print(f'\033[1mCognito User Pool Name:\033[0m {provider_details.get("user_pool_name", "N/A")}') | ||
print(f'\033[1mCognito User Pool ID:\033[0m {provider_details.get("user_pool_id", "N/A")}') | ||
print(f'\033[1mClient ID:\033[0m {provider_details.get("client_id", "N/A")}') | ||
print(f'\033[1mDynamoDB Table:\033[0m {provider_details.get("dynamodb_table", "N/A")}\n') | ||
else: | ||
print('No Provider user pool found.\n') | ||
|
||
# Print Staff User Pool Details | ||
print('\033[1;36m=== Staff Users ===\033[0m') # Cyan header | ||
if staff_details: | ||
print(f'\033[1mLogin URL:\033[0m {staff_details.get("login_url", "N/A")}') | ||
print(f'\033[1mCognito User Pool Name:\033[0m {staff_details.get("user_pool_name", "N/A")}') | ||
print(f'\033[1mCognito User Pool ID:\033[0m {staff_details.get("user_pool_id", "N/A")}') | ||
print(f'\033[1mClient ID:\033[0m {staff_details.get("client_id", "N/A")}') | ||
print(f'\033[1mDynamoDB Table:\033[0m {staff_details.get("dynamodb_table", "N/A")}\n') | ||
else: | ||
print('No Staff user pool found.\n') | ||
|
||
|
||
def print_env_format(api_gateway_url, provider_details, staff_details): | ||
"""Prints data in .env format""" | ||
login_url = provider_details.get('login_url', 'N/A').removesuffix('/login') | ||
staff_client_id = staff_details.get('client_id', 'N/A') | ||
provider_client_id = provider_details.get('client_id', 'N/A') | ||
staff_table = staff_details.get('dynamodb_table', 'N/A') | ||
provider_table = provider_details.get('dynamodb_table', 'N/A') | ||
|
||
print(f'VUE_APP_API_STATE_ROOT={api_gateway_url}') | ||
print(f'VUE_APP_API_LICENSE_ROOT={api_gateway_url}') | ||
print(f'VUE_APP_API_USER_ROOT={api_gateway_url}') | ||
print(f'VUE_APP_COGNITO_REGION={aws_region}') | ||
print(f'VUE_APP_COGNITO_AUTH_DOMAIN_STAFF={login_url}') | ||
print(f'VUE_APP_COGNITO_CLIENT_ID_STAFF={staff_client_id}') | ||
print(f'VUE_APP_COGNITO_AUTH_DOMAIN_LICENSEE={login_url}') | ||
print(f'VUE_APP_COGNITO_CLIENT_ID_LICENSEE={provider_client_id}') | ||
print(f'VUE_APP_DYNAMO_TABLE_PROVIDER={provider_table}') | ||
print(f'VUE_APP_DYNAMO_TABLE_STAFF={staff_table}') | ||
|
||
|
||
if __name__ == '__main__': | ||
# Argument parser for --as-env flag | ||
parser = argparse.ArgumentParser(description='Fetch AWS resource details.') | ||
parser.add_argument('--as-env', action='store_true', help='Output in .env format') | ||
args = parser.parse_args() | ||
|
||
# Fetch resources | ||
api_gateway_url, provider_details, staff_details = fetch_resources() | ||
|
||
# Output in the requested format | ||
if args.as_env: | ||
print_env_format(api_gateway_url, provider_details, staff_details) | ||
else: | ||
print_human_readable(api_gateway_url, provider_details, staff_details) |
This file contains hidden or 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
Empty file.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.