Skip to content

Commit cffc2de

Browse files
feat: add NPM frontend deployment workflow and update license workflow (#14)
This PR introduces a new NPM frontend deployment workflow and updates the license management workflow, along with corresponding documentation updates in the README. Adds a new workflow to deploy NPM-based frontend applications to AWS S3 and CloudFront. Updates the license management workflow to leverage GitHub App credentials for secure license updates. Revises the README to reflect the new workflows and changes.
1 parent dde0fc3 commit cffc2de

5 files changed

Lines changed: 353 additions & 54 deletions

File tree

.github/CODEOWNERS

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,4 @@
44
# More details are here: https://help.github.com/articles/about-codeowners/
55

66
# The '*' pattern is global owners.
7-
8-
# Order is important. The last matching pattern has the most precedence.
9-
# The folders are ordered as follows:
10-
11-
# In each subsection folders are ordered first by depth, then alphabetically.
12-
# This should make it easy to add new rules without breaking existing ones.
13-
14-
# Workflows
15-
/.github/* @stefano-franco @nuvibit-team
16-
/.github/workflows/* @stefano-franco @nuvibit-team
7+
# There are currently no codeowners defined.

.github/workflows/drawio-export.yml

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ on:
88
default: "."
99
required: false
1010
type: string
11+
commit_user:
12+
description: 'Username which should be used for commits by github action'
13+
default: 'github-actions'
14+
required: false
15+
type: string
16+
commit_email:
17+
description: 'Email which should be used for commits by github action'
18+
default: 'noreply@github.com'
19+
required: false
20+
type: string
1121

1222
concurrency:
1323
group: drawio-export-${{ github.ref }}
@@ -35,15 +45,20 @@ jobs:
3545
quality: 95
3646
uncompressed: true
3747

38-
- name: Get author and committer info from HEAD commit
39-
uses: rlespinasse/git-commit-data-action@v1.x
40-
if: github.ref == 'refs/heads/main'
48+
- name: Commit Changes
49+
run: |
50+
git config --local user.name "${{ inputs.commit_user }}"
51+
git config --local user.email "${{ inputs.commit_email }}"
52+
git add .
53+
if [[ -z $(git status -s) ]]; then
54+
echo "diff=false" >> $GITHUB_OUTPUT
55+
else
56+
echo "diff=true" >> $GITHUB_OUTPUT
57+
fi
58+
git diff-index --quiet HEAD || git commit -m "docs: sync draw.io exported files" -a
4159
42-
- name: Commit changed files
43-
uses: stefanzweifel/git-auto-commit-action@v5
60+
- name: Push Changes
61+
uses: ad-m/github-push-action@v0.8.0
4462
with:
45-
commit_message: "docs: sync draw.io exported files"
46-
commit_user_name: "${{ env.GIT_COMMIT_COMMITTER_NAME }}"
47-
commit_user_email: "${{ env.GIT_COMMIT_COMMITTER_EMAIL }}"
48-
commit_author: "${{ env.GIT_COMMIT_AUTHOR }}"
49-
if: github.ref == 'refs/heads/main'
63+
github_token: ${{ steps.github_app_token.outputs.token }}
64+
branch: ${{ github.ref }}
Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1-
name: NTC WORKFLOW
1+
name: LICENSE UPDATE
22

33
on:
44
workflow_call:
55
inputs:
66
license_file_source_repo:
7-
description: 'Repository where license file is stored.'
8-
default: "nuvibit-terraform-collection/module.tpl"
9-
required: false
7+
description: 'Repository where license file is stored (needs to be public or in the same Github organization).'
8+
required: true
109
type: string
1110
license_file_name:
1211
description: 'License file name.'
1312
default: "LICENSE"
1413
required: false
1514
type: string
15+
company_name:
16+
description: 'Company name to use in license header.'
17+
default: "Nuvibit AG"
18+
required: false
19+
type: string
1620
insert_header_file_extension:
1721
description: 'File extension of files where license header should be added.'
1822
default: ".tf"
@@ -29,30 +33,45 @@ on:
2933
required: false
3034
type: string
3135
secrets:
32-
GHE_API_TOKEN:
36+
# GitHub App credentials to avoid using GITHUB_TOKEN
37+
GH_APP_ID:
38+
required: true
39+
GH_APP_PRIVATE_KEY:
3340
required: true
3441

3542
jobs:
36-
ntc-license:
43+
license-update:
3744
runs-on: ubuntu-latest
3845

3946
steps:
47+
- name: Get Github Access Token
48+
id: github_app_token
49+
uses: actions/create-github-app-token@v2
50+
with:
51+
app-id: ${{ secrets.GH_APP_ID }}
52+
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
53+
# if owner and repositories are empty, access will be scoped to only the current repository
54+
owner: ${{ github.repository_owner }}
55+
repositories: |
56+
${{ inputs.license_file_source_repo }}
57+
${{ github.event.repository.name }}
58+
4059
- name: Checkout
4160
uses: actions/checkout@v4
4261
with:
4362
ref: ${{ github.event.pull_request.head.ref }}
44-
token: ${{ secrets.GHE_API_TOKEN }}
63+
token: ${{ steps.github_app_token.outputs.token }}
4564

4665
- name: Checkout License File
4766
uses: actions/checkout@v4
4867
with:
49-
repository: ${{ inputs.license_file_source_repo }}
68+
repository: ${{ github.repository_owner }}/${{ inputs.license_file_source_repo }}
5069
path: remote_repo
5170
persist-credentials: false
5271
sparse-checkout: |
5372
${{ inputs.license_file_name }}
5473
sparse-checkout-cone-mode: false
55-
token: ${{ secrets.GHE_API_TOKEN }}
74+
token: ${{ steps.github_app_token.outputs.token }}
5675

5776
- name: Update License File
5877
run: |
@@ -68,7 +87,7 @@ jobs:
6887
sed -i '/^# Copyright/d' $file
6988
sed -i '/^# SPDX-License-Identifier/d' $file
7089
sed -i '/./,$!d' $file
71-
sed -i '1s/^/# Copyright (c) Nuvibit AG\n# SPDX-License-Identifier: see terms and conditions in the LICENSE file\n\n/' $file
90+
sed -i '1s/^/# Copyright (c) ${{ inputs.company_name }}\n# SPDX-License-Identifier: see terms and conditions in the LICENSE file\n\n/' $file
7291
done
7392
7493
- name: Commit Changes
@@ -86,5 +105,5 @@ jobs:
86105
- name: Push Changes
87106
uses: ad-m/github-push-action@v0.8.0
88107
with:
89-
github_token: ${{ secrets.GITHUB_TOKEN }}
108+
github_token: ${{ steps.github_app_token.outputs.token }}
90109
branch: ${{ github.event.pull_request.head.ref }}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
name: NPM FRONTEND DEPLOYMENT
2+
3+
on:
4+
workflow_call:
5+
inputs:
6+
node_version:
7+
description: 'Node.js version to use for the build (default: lts/*)'
8+
default: 'lts/*'
9+
required: false
10+
type: string
11+
aws_default_region:
12+
description: 'AWS region to use for deployment (default: eu-central-1)'
13+
default: 'eu-central-1'
14+
required: false
15+
type: string
16+
aws_oidc_role_arn:
17+
description: 'AWS OIDC IAM role ARN to assume for deployment (e.g. arn:aws:iam::111111111111:role/oidc-deployment-role)'
18+
required: true
19+
type: string
20+
domain_name:
21+
description: 'Domain name used for the frontend application (e.g. docs.nuvibit.com)'
22+
required: true
23+
type: string
24+
s3_deployment_dir:
25+
description: 'S3 directory where the build artifacts will be uploaded (default: build)'
26+
default: 'build'
27+
required: false
28+
type: string
29+
local_build_dir:
30+
description: 'Local directory where the build artifacts are located (default: build)'
31+
default: 'build'
32+
required: false
33+
type: string
34+
cloudfront_invalidate_paths:
35+
description: 'Paths to invalidate in CloudFront after deployment (ALL, UPDATED, NONE)'
36+
required: true
37+
type: string
38+
# ALL: Invalidate all paths (default)
39+
# UPDATED: Invalidate only paths that were updated
40+
# NONE: Do not invalidate any paths
41+
default: 'ALL'
42+
43+
jobs:
44+
npm-frontend-deploy:
45+
name: Deploy Frontend
46+
runs-on: ubuntu-latest
47+
permissions:
48+
id-token: write
49+
contents: read
50+
steps:
51+
- uses: actions/checkout@v4
52+
with:
53+
fetch-depth: 0
54+
55+
- uses: actions/setup-node@v4
56+
with:
57+
node-version: ${{ inputs.node_version }}
58+
59+
- name: Install dependencies
60+
run: npm install
61+
62+
- name: Build website
63+
run: npm run build
64+
65+
- name: Configure AWS credentials
66+
uses: aws-actions/configure-aws-credentials@v4
67+
with:
68+
role-to-assume: ${{ inputs.aws_oidc_role_arn }}
69+
aws-region: ${{ inputs.aws_default_region }}
70+
71+
- name: Upload to S3 and track changed files
72+
id: sync
73+
run: |
74+
# First identify which files will be synced
75+
SYNC_OUTPUT=$(aws s3 sync ./${{ inputs.local_build_dir }} s3://${{ inputs.domain_name }}/${{ inputs.s3_deployment_dir }} --dryrun)
76+
echo "Found changes to sync."
77+
78+
# Extract file paths that will be uploaded/updated
79+
PATHS_TO_INVALIDATE=$(echo "$SYNC_OUTPUT" | grep -E 'upload:|update:' | sed -E 's/^\(dryrun\) (upload:|update:) (.*) to ([^ ]+).*/\3/' | sed 's|s3://${{ inputs.domain_name }}/${{ inputs.s3_deployment_dir }}||')
80+
echo "Paths to be updated:"
81+
echo "$PATHS_TO_INVALIDATE"
82+
83+
# Save paths for invalidation, adjust format for JSON array
84+
if [ -n "$PATHS_TO_INVALIDATE" ]; then
85+
PATHS_JSON=$(echo "$PATHS_TO_INVALIDATE" | sed 's/^/"/' | sed 's/$/"/' | paste -sd, -)
86+
echo "paths_json=[$PATHS_JSON]" >> $GITHUB_OUTPUT
87+
echo "has_changes=true" >> $GITHUB_OUTPUT
88+
else
89+
echo "No changes to invalidate"
90+
echo "has_changes=false" >> $GITHUB_OUTPUT
91+
fi
92+
93+
# Perform the actual sync
94+
aws s3 sync ./${{ inputs.local_build_dir }} s3://${{ inputs.domain_name }}/${{ inputs.s3_deployment_dir }}
95+
96+
- name: Find CloudFront distribution ID
97+
id: cloudfront
98+
if: steps.sync.outputs.has_changes == 'true'
99+
run: |
100+
# Find the CloudFront distribution that has our S3 bucket as origin
101+
DISTRIBUTION_ID=$(aws cloudfront list-distributions --query "DistributionList.Items[?contains(Origins.Items[].DomainName, '${{ inputs.domain_name }}.s3.${{ inputs.aws_default_region }}.amazonaws.com')].Id" --output text)
102+
103+
if [ -z "$DISTRIBUTION_ID" ]; then
104+
echo "::error::Could not find CloudFront distribution for ${{ inputs.domain_name }}"
105+
exit 1
106+
fi
107+
108+
echo "Found CloudFront distribution: $DISTRIBUTION_ID"
109+
echo "distribution_id=$DISTRIBUTION_ID" >> $GITHUB_OUTPUT
110+
111+
- name: Invalidate CloudFront cache for ALL PATHS
112+
if: steps.sync.outputs.has_changes == 'true' && inputs.cloudfront_invalidate_paths == 'ALL'
113+
run: |
114+
# Create CloudFront invalidation for all paths
115+
DISTRIBUTION_ID=${{ steps.cloudfront.outputs.distribution_id }}
116+
117+
echo "Creating CloudFront invalidation for paths: /*"
118+
aws cloudfront create-invalidation --distribution-id $DISTRIBUTION_ID --paths "/*"
119+
120+
- name: Invalidate CloudFront cache for UPDATED PATHS
121+
if: steps.sync.outputs.has_changes == 'true' && inputs.cloudfront_invalidate_paths == 'UPDATED'
122+
run: |
123+
# Create CloudFront invalidation for the paths that were updated
124+
PATHS=${{ toJSON(steps.sync.outputs.paths_json) }}
125+
DISTRIBUTION_ID=${{ steps.cloudfront.outputs.distribution_id }}
126+
127+
echo "Creating Invalidation batch file"
128+
echo "{\"Paths\": {\"Quantity\": $(echo $PATHS | jq length), \"Items\": $PATHS}, \"CallerReference\": \"$(date +%s)\"}" > inv-batch.json
129+
echo "Creating CloudFront invalidation for paths: $PATHS"
130+
aws cloudfront create-invalidation --distribution-id $DISTRIBUTION_ID --invalidation-batch file://inv-batch.json

0 commit comments

Comments
 (0)