ci: move pages tf logic to use modules (#66)

This commit is contained in:
Zack Pollard
2024-11-26 14:35:37 +00:00
committed by GitHub
parent e1e7580b2a
commit feb645ba45
12 changed files with 118 additions and 259 deletions
+78 -3
View File
@@ -11,6 +11,11 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
tofu_version: '1.7.1'
tg_version: '0.69.0'
ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'prod' || 'dev' }}
jobs:
build:
name: Build
@@ -19,9 +24,9 @@ jobs:
matrix:
include:
- app: my.immich.app
name: my-immich
name: my
- app: buy.immich.app
name: buy-immich
name: buy
steps:
- name: Checkout code
@@ -38,7 +43,7 @@ jobs:
echo IMMICH_APP="${{ matrix.app }}" >> .env
- name: Use futopay-test
if: ${{ github.event_name == 'pull_request' }}
if: ${{ env.ENVIRONMENT == 'dev' }}
run: |
echo PUBLIC_IMMICH_PAY_HOST=https://futopay-test.azurewebsites.net >> .env
@@ -54,3 +59,73 @@ jobs:
name: build-output-${{ matrix.name }}
path: build
retention-days: 1
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: build
strategy:
matrix:
include:
- app: my.immich.app
name: my
- app: buy.immich.app
name: buy
env:
TF_VAR_app_name: ${{ matrix.name }}
TF_VAR_dist_dir: ${{ github.workspace }}/dist
TF_VAR_stage: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || '' }}
OP_SERVICE_ACCOUNT_TOKEN: ${{ github.ref == 'refs/heads/main' && secrets.OP_TF_PROD_ENV || secrets.OP_TF_DEV_ENV }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: 'Get build artifact'
uses: actions/download-artifact@v4
with:
name: 'build-output-${{ matrix.name }}'
path: '${{ github.workspace }}/build'
- name: Install 1Password CLI
uses: 1password/install-cli-action@v1
- name: Install Terragrunt
uses: eLco/setup-terragrunt@v1
with:
terragrunt_version: ${{ env.tg_version }}
- name: 'Install OpenTofu'
uses: opentofu/setup-opentofu@v1
with:
tofu_version: ${{ env.tofu_version }}
tofu_wrapper: false
- name: Deploy All
working-directory: ${{ github.workspace }}/deployment
run: op run --env-file=".env" -- terragrunt run-all apply --terragrunt-non-interactive
- name: Cloudflare Deploy Output
id: deploy-output
working-directory: ${{ github.workspace }}/deployment/modules/cloudflare/static-pages
run: |
echo "output=$(op run --no-masking --env-file='../../../.env' -- terragrunt output -json | jq -c .)" >> $GITHUB_OUTPUT
- name: Publish Frontend to Cloudflare Pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: ${{ fromJson(steps.deploy-output.outputs.output).pages_project_name.value }}
directory: 'build'
branch: ${{ fromJson(steps.deploy-output.outputs.output).pages_branch.value }}
wranglerVersion: '3'
- name: Comment
uses: actions-cool/maintain-one-comment@v3
if: ${{ github.event_name == 'pull_request' }}
with:
number: ${{ github.event.number }}
body: |
📖 Preview of ${{ matrix.app }} deployed to [${{ fromJson(steps.deploy-output.outputs.output).immich_subdomain.value }}](https://${{ fromJson(steps.deploy-output.outputs.output).immich_subdomain.value }})
emojis: 'rocket'
body-include: '<!-- ${{ matrix.app }} PR URL -->'
-194
View File
@@ -1,194 +0,0 @@
name: Deploy
on:
workflow_run:
workflows: ['Build']
types:
- completed
jobs:
checks:
name: Deploy checks
runs-on: ubuntu-latest
strategy:
matrix:
include:
- app: my.immich.app
name: my-immich
- app: buy.immich.app
name: buy-immich
outputs:
parameters: ${{ steps.parameters.outputs.result }}
steps:
- if: ${{ github.event.workflow_run.conclusion == 'failure' }}
run: echo 'The triggering workflow failed' && exit 1
- name: Determine deploy parameters
id: parameters
uses: actions/github-script@v7
with:
script: |
const eventType = context.payload.workflow_run.event;
const isFork = context.payload.workflow_run.repository.fork;
let parameters;
console.log({eventType, isFork});
if (eventType == "push") {
const branch = context.payload.workflow_run.head_branch;
console.log({branch});
const shouldDeploy = !isFork && branch == "main";
parameters = {
event: "branch",
name: "main",
shouldDeploy
};
} else if (eventType == "pull_request") {
let pull_number = context.payload.workflow_run.pull_requests[0]?.number;
if(!pull_number) {
const response = await github.rest.search.issuesAndPullRequests({q: 'repo:${{ github.repository }} is:pr sha:${{ github.event.workflow_run.head_sha }}',per_page: 1,})
const items = response.data.items
if (items.length < 1) {
throw new Error("No pull request found for the commit")
}
const pullRequestNumber = items[0].number
console.info("Pull request number is", pullRequestNumber)
pull_number = pullRequestNumber
}
const {data: pr} = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number
});
console.log({pull_number});
parameters = {
event: "pr",
name: `pr-${pull_number}`,
pr_number: pull_number,
shouldDeploy: true
};
} else if (eventType == "release") {
parameters = {
event: "release",
name: context.payload.workflow_run.head_branch,
shouldDeploy: !isFork
};
}
console.log(parameters);
return parameters;
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: checks
strategy:
matrix:
include:
- app: my.immich.app
name: my-immich
- app: buy.immich.app
name: buy-immich
if: ${{ fromJson(needs.checks.outputs.parameters).shouldDeploy }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Load parameters
id: parameters
uses: actions/github-script@v7
with:
script: |
const json = `${{ needs.checks.outputs.parameters }}`;
const parameters = JSON.parse(json);
core.setOutput("event", parameters.event);
core.setOutput("name", parameters.name);
core.setOutput("shouldDeploy", parameters.shouldDeploy);
- run: |
echo "Starting docs deployment for ${{ steps.parameters.outputs.event }} ${{ steps.parameters.outputs.name }}"
- name: Download artifact
uses: actions/github-script@v7
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "build-output-${{ matrix.name }}"
})[0];
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/build-output.zip`, Buffer.from(download.data));
- name: Unzip artifact
run: unzip "${{ github.workspace }}/build-output.zip" -d "${{ github.workspace }}/build"
- name: Deploy App
env:
TF_VAR_prefix_name: ${{ steps.parameters.outputs.name }}
TF_VAR_prefix_event_type: ${{ steps.parameters.outputs.event }}
TF_VAR_app_url: ${{ matrix.app }}
TF_VAR_app_name: ${{ matrix.name }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
uses: gruntwork-io/terragrunt-action@v2
with:
tg_version: '0.58.12'
tofu_version: '1.7.1'
tg_dir: 'deployment/modules/cloudflare/static-pages'
tg_command: 'apply'
- name: Deploy App Output
id: terraform-output
env:
TF_VAR_prefix_name: ${{ steps.parameters.outputs.name }}
TF_VAR_prefix_event_type: ${{ steps.parameters.outputs.event }}
TF_VAR_app_url: ${{ matrix.app }}
TF_VAR_app_name: ${{ matrix.name }}
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
TF_STATE_POSTGRES_CONN_STR: ${{ secrets.TF_STATE_POSTGRES_CONN_STR }}
uses: gruntwork-io/terragrunt-action@v2
with:
tg_version: '0.58.12'
tofu_version: '1.7.1'
tg_dir: 'deployment/modules/cloudflare/static-pages'
tg_command: 'output -json'
- name: Output Cleaning
id: clean
run: |
TG_OUT=$(echo '${{ steps.terraform-output.outputs.tg_action_output }}' | sed 's|%0A|\n|g ; s|%3C|<|g' | jq -c .)
echo "output=$TG_OUT" >> $GITHUB_OUTPUT
- name: Publish to Cloudflare Pages
uses: cloudflare/pages-action@v1
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN_PAGES_UPLOAD }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
projectName: ${{ fromJson(steps.clean.outputs.output).pages_project_name.value }}
directory: 'build'
branch: ${{ steps.parameters.outputs.name }}
wranglerVersion: '3'
- name: Comment
uses: actions-cool/maintain-one-comment@v3
if: ${{ steps.parameters.outputs.event == 'pr' }}
with:
number: ${{ fromJson(needs.checks.outputs.parameters).pr_number }}
body: |
📖 Preview of ${{ matrix.app }} deployed to [${{ fromJson(steps.clean.outputs.output).immich_app_branch_subdomain.value }}](https://${{ fromJson(steps.clean.outputs.output).immich_app_branch_subdomain.value }})
emojis: 'rocket'
body-include: '<!-- ${{ matrix.app }} PR URL -->'
-1
View File
@@ -11,7 +11,6 @@ node_modules
Thumbs.db
# Env
.env
.env.*
!.env.example
!.env.test
+4
View File
@@ -0,0 +1,4 @@
export TF_VAR_cloudflare_account_id="op://tf/cloudflare/account_id"
export TF_VAR_cloudflare_api_token="op://tf/cloudflare/api_token"
export TF_VAR_tf_state_postgres_conn_str="op://tf/tf_state/postgres_conn_str"
export TF_VAR_env=$ENVIRONMENT
@@ -5,20 +5,7 @@ provider "registry.opentofu.org/cloudflare/cloudflare" {
version = "4.46.0"
constraints = "4.46.0"
hashes = [
"h1:3U4N3bbMacXTAdyaEwT305kETMETh1jZmGApmN6gdyE=",
"h1:3fhZhGNgtS9ugcZ2CIH6kk8LzN6yPxqOdkDUZqkP3+w=",
"h1:JWluJxBRSr8GVUhWVv83xse9SmbpwCLctCDddMXUnVk=",
"h1:KDHwakGt+3iBKXaoALCCAolPaJgpEHbkh3BfjnpuqoM=",
"h1:QFFZshAvwr9L5TQmsNQC6/sDqokk5pjbP8Ae4BQqMLQ=",
"h1:Qdi+vXwzDNii7ytSaOQtnlqhjZ3ZlRoUkFoi6CD2COI=",
"h1:TPcJXcVb/+C91hUuu8CEn98QUoNgLtnHfd4sgAOV+5k=",
"h1:WDy5wiNroXaCnw+r8rJnCP+J1RVsm2Qu3AOZ/iV4lLo=",
"h1:hMuL+dwHj3JbePqYcDrn/ZQN9R0WzeJX0AIDJ02Iteo=",
"h1:hQKCaUEARzJKbFt1CePP06E/+CiHWe/H6lc1AwK7y6w=",
"h1:l4DQ3WXmSzR/GBel3m2CRKWtaziVjBoxvUgL63t1GK0=",
"h1:nN9uVSLyrb/DjfZl6rPtCq5j0TX+6WypzNDexdzCQ08=",
"h1:rAX7njl6lKT9XIKMk6pLjVi7u/42wafRolWWgMHMkI0=",
"h1:t2IQYNu8YNykqYlEB+TTX+XpUd5z2flwGw8km9UgbnQ=",
"zh:2ee426ef3389022db0026792fdc4f2980dcf2600e31adf5a31b4bddfa8d68343",
"zh:2f993edb23df55dc1c18150fa187d80aa7d87e6439698ee34b6a6aad23ac2dd7",
"zh:3d6601333975e55979b1b454e50ff9a482ce4e0269dd6c72a50202163a8f4463",
@@ -1,26 +0,0 @@
resource "cloudflare_pages_domain" "immich_app_branch_domain" {
account_id = var.cloudflare_account_id
project_name = data.terraform_remote_state.cloudflare_account.outputs.static_pages_project_names[var.app_url]
domain = local.domain
}
resource "cloudflare_record" "immich_app_branch_subdomain" {
name = local.domain
proxied = true
ttl = 1
type = "CNAME"
value = local.is_main ? data.terraform_remote_state.cloudflare_account.outputs.static_pages_project_subdomains[var.app_url] : "${replace(var.prefix_name, "/\\/|\\./", "-")}.${data.terraform_remote_state.cloudflare_account.outputs.static_pages_project_subdomains[var.app_url]}"
zone_id = data.terraform_remote_state.cloudflare_account.outputs.immich_app_zone_id
}
output "immich_app_branch_subdomain" {
value = cloudflare_record.immich_app_branch_subdomain.hostname
}
output "immich_app_branch_pages_hostname" {
value = cloudflare_record.immich_app_branch_subdomain.value
}
output "pages_project_name" {
value = cloudflare_pages_domain.immich_app_branch_domain.project_name
}
@@ -1,5 +0,0 @@
locals {
is_main = var.prefix_name == "main"
domain_prefix = !local.is_main && contains(["branch", "pr"], var.prefix_event_type) ? "${var.prefix_name}.preview." : ""
domain = "${local.domain_prefix}${var.app_url}"
}
@@ -0,0 +1,28 @@
module "static_pages" {
source = "git::https://github.com/immich-app/devtools.git//tf/shared/modules/cloudflare-pages?ref=main"
cloudflare_api_token = data.terraform_remote_state.api_keys_state.outputs.terraform_key_cloudflare_docs
cloudflare_account_id = data.terraform_remote_state.cloudflare_account.outputs.cloudflare_account_id
pages_project = data.terraform_remote_state.cloudflare_account.outputs.pages_projects["${var.app_name}.immich.app"]
app_name = var.app_name
stage = var.stage
env = var.env
}
output "pages_branch" {
value = module.static_pages.pages_branch
}
output "immich_subdomain" {
value = module.static_pages.branch_subdomain
}
output "pages_branch_subdomain" {
value = module.static_pages.pages_branch_subdomain
}
output "pages_project_name" {
value = module.static_pages.pages_project_name
}
@@ -1,3 +0,0 @@
provider "cloudflare" {
api_token = data.terraform_remote_state.api_keys_state.outputs.terraform_key_cloudflare_docs
}
@@ -11,7 +11,8 @@ include {
}
locals {
prefix_name = get_env("TF_VAR_prefix_name")
env = get_env("TF_VAR_env")
stage = get_env("TF_VAR_stage")
app_name = replace(get_env("TF_VAR_app_name"), "-", "_")
}
@@ -19,7 +20,7 @@ remote_state {
backend = "pg"
config = {
conn_str = get_env("TF_STATE_POSTGRES_CONN_STR")
schema_name = "prod_cloudflare_immich_app_${local.app_name}_${local.prefix_name}"
conn_str = get_env("TF_VAR_tf_state_postgres_conn_str")
schema_name = "prod_cloudflare_immich_app_${local.app_name}_${local.env}${local.stage}"
}
}
@@ -1,6 +1,4 @@
variable "cloudflare_account_id" {}
variable "tf_state_postgres_conn_str" {}
variable "prefix_name" {}
variable "prefix_event_type" {}
variable "app_url" {}
variable "stage" {}
variable "env" {}
variable "app_name" {}
+1 -6
View File
@@ -1,8 +1,5 @@
locals {
cloudflare_account_id = get_env("CLOUDFLARE_ACCOUNT_ID")
cloudflare_api_token = get_env("CLOUDFLARE_API_TOKEN")
tf_state_postgres_conn_str = get_env("TF_STATE_POSTGRES_CONN_STR")
tf_state_postgres_conn_str = get_env("TF_VAR_tf_state_postgres_conn_str")
}
remote_state {
@@ -14,7 +11,5 @@ remote_state {
}
inputs = {
cloudflare_account_id = local.cloudflare_account_id
cloudflare_api_token = local.cloudflare_api_token
tf_state_postgres_conn_str = local.tf_state_postgres_conn_str
}