mirror of
https://github.com/immich-app/static-pages.git
synced 2026-09-30 13:23:05 +08:00
ci: move pages tf logic to use modules (#66)
This commit is contained in:
@@ -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 -->'
|
||||
|
||||
@@ -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 -->'
|
||||
@@ -11,7 +11,6 @@ node_modules
|
||||
Thumbs.db
|
||||
|
||||
# Env
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
!.env.test
|
||||
|
||||
@@ -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,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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user