mirror of
https://github.com/immich-app/ui.git
synced 2026-09-30 13:32:54 +08:00
feat: preview environments and automated deployments (#32)
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
|
||||
@@ -30,3 +35,71 @@ jobs:
|
||||
|
||||
- name: Run build
|
||||
run: npm run build
|
||||
|
||||
- name: Upload build output
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: build-output
|
||||
path: build
|
||||
retention-days: 1
|
||||
|
||||
deploy:
|
||||
name: Deploy
|
||||
runs-on: ubuntu-latest
|
||||
needs: build
|
||||
env:
|
||||
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'
|
||||
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 ui.immich.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: '<!-- ui PR URL -->'
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
name: Destroy
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [closed]
|
||||
|
||||
env:
|
||||
tofu_version: '1.7.1'
|
||||
tg_version: '0.69.0'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
max-parallel: 1
|
||||
matrix:
|
||||
environment: ['dev', 'prod']
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- 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: Destroy All
|
||||
working-directory: ${{ env.working_dir }}
|
||||
env:
|
||||
ENVIRONMENT: ${{ matrix.environment }}
|
||||
TF_VAR_stage: pr-${{ github.event.number }}
|
||||
OP_SERVICE_ACCOUNT_TOKEN: ${{ matrix.environment == 'prod' && secrets.OP_TF_PROD_ENV || secrets.OP_TF_DEV_ENV }}
|
||||
working_dir: 'deployment'
|
||||
run: op run --env-file=".env" -- terragrunt run-all destroy -refresh=false --terragrunt-non-interactive --terragrunt-exclude-dir '**/pages-project'
|
||||
|
||||
- name: Comment
|
||||
uses: actions-cool/maintain-one-comment@v3
|
||||
with:
|
||||
number: ${{ github.event.number }}
|
||||
delete: true
|
||||
body-include: '<!-- web PR URL -->'
|
||||
@@ -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
|
||||
@@ -0,0 +1,40 @@
|
||||
# OpenTofu
|
||||
|
||||
!.env
|
||||
|
||||
# Local .terraform directories
|
||||
**/.terraform/*
|
||||
|
||||
# .tfstate files
|
||||
*.tfstate
|
||||
*.tfstate.*
|
||||
|
||||
# Crash log files
|
||||
crash.log
|
||||
crash.*.log
|
||||
|
||||
# Ignore override files as they are usually used to override resources locally and so
|
||||
# are not checked in
|
||||
override.tf
|
||||
override.tf.json
|
||||
*_override.tf
|
||||
*_override.tf.json
|
||||
|
||||
# Include override files you do wish to add to version control using negated pattern
|
||||
# !example_override.tf
|
||||
|
||||
# Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan
|
||||
# example: *tfplan*
|
||||
|
||||
# Ignore CLI configuration files
|
||||
.terraformrc
|
||||
terraform.rc
|
||||
|
||||
# Terragrunt
|
||||
|
||||
# terragrunt cache directories
|
||||
**/.terragrunt-cache/*
|
||||
|
||||
# Terragrunt debug output file (when using `--terragrunt-debug` option)
|
||||
# See: https://terragrunt.gruntwork.io/docs/reference/cli-options/#terragrunt-debug
|
||||
terragrunt-debug.tfvars.json
|
||||
@@ -0,0 +1,25 @@
|
||||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||
version = "4.46.0"
|
||||
constraints = "4.46.0"
|
||||
hashes = [
|
||||
"h1:QFFZshAvwr9L5TQmsNQC6/sDqokk5pjbP8Ae4BQqMLQ=",
|
||||
"zh:2ee426ef3389022db0026792fdc4f2980dcf2600e31adf5a31b4bddfa8d68343",
|
||||
"zh:2f993edb23df55dc1c18150fa187d80aa7d87e6439698ee34b6a6aad23ac2dd7",
|
||||
"zh:3d6601333975e55979b1b454e50ff9a482ce4e0269dd6c72a50202163a8f4463",
|
||||
"zh:4e5f48dce22f7a6d618018d65d1d443bb718defa23f514d5c6385860541fbe79",
|
||||
"zh:5ebf5aea960fc30de381ffd6db20876d249673cf938fe67f1dfb6b9caa1db418",
|
||||
"zh:80ed3fb901141f53b4b56ddb7eea5f2e0c0830d501387539d2c2b8e0cc7e587a",
|
||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||
"zh:9aeae8b3be4a577ced46987fd9159262c5b4c54a510f66592fbcdb40fef55b10",
|
||||
"zh:a0479ef2d308c4a7894f1fe77467cd07e04c7b40d281088f4f204af1bdf94ac6",
|
||||
"zh:a2bdc0c25130665af0b9559942b9813a1ba4889513e7185d4abc9c02e9bb99bd",
|
||||
"zh:b10be9755fe80395ced6f0bbda38b8c8681714cf1eca1d895be239c75c2ffc2a",
|
||||
"zh:ba3d55e722d9f48646574ce7c448f0084fe21fa884b5f8b6d6146a82a99c4baa",
|
||||
"zh:ec1fd0ecaedc787a77d5342b51ae8dea8362a67f1e19123f6521a0e8e012d9e8",
|
||||
"zh:ed49590e69faef14550179f965b4451b31415b8f6be6d33427ad48f65c76b6cf",
|
||||
"zh:f4baa3a2dac719ad20dcfa525bc3f737ad95650b8d0de0c648dc9a87f993b2c3",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
terraform {
|
||||
backend "pg" {}
|
||||
required_version = "~> 1.7"
|
||||
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "4.46.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
locals {
|
||||
app_name = "ui"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
module "pages_project" {
|
||||
source = "git::https://github.com/immich-app/devtools.git//tf/shared/modules/cloudflare-pages-project?ref=feat/pages-project-tf-module"
|
||||
|
||||
cloudflare_api_token = data.terraform_remote_state.api_keys_state.outputs.terraform_key_cloudflare_account
|
||||
cloudflare_account_id = data.terraform_remote_state.cloudflare_account.outputs.cloudflare_account_id
|
||||
|
||||
app_name = local.app_name
|
||||
env = var.env
|
||||
}
|
||||
|
||||
output "pages_project" {
|
||||
value = module.pages_project.pages_project
|
||||
sensitive = true
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
data "terraform_remote_state" "api_keys_state" {
|
||||
backend = "pg"
|
||||
|
||||
config = {
|
||||
conn_str = var.tf_state_postgres_conn_str
|
||||
schema_name = "prod_cloudflare_api_keys"
|
||||
}
|
||||
}
|
||||
|
||||
data "terraform_remote_state" "cloudflare_account" {
|
||||
backend = "pg"
|
||||
|
||||
config = {
|
||||
conn_str = var.tf_state_postgres_conn_str
|
||||
schema_name = "prod_cloudflare_account"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
terraform {
|
||||
source = "."
|
||||
|
||||
extra_arguments custom_vars {
|
||||
commands = get_terraform_commands_that_need_vars()
|
||||
}
|
||||
}
|
||||
|
||||
include {
|
||||
path = find_in_parent_folders("state.hcl")
|
||||
}
|
||||
|
||||
locals {
|
||||
env = get_env("TF_VAR_env")
|
||||
}
|
||||
|
||||
remote_state {
|
||||
backend = "pg"
|
||||
|
||||
config = {
|
||||
conn_str = get_env("TF_VAR_tf_state_postgres_conn_str")
|
||||
schema_name = "cloudflare_pages_project_immich_app_ui_${local.env}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
variable "tf_state_postgres_conn_str" {}
|
||||
variable "env" {}
|
||||
@@ -0,0 +1,25 @@
|
||||
# This file is maintained automatically by "tofu init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.opentofu.org/cloudflare/cloudflare" {
|
||||
version = "4.46.0"
|
||||
constraints = "4.46.0"
|
||||
hashes = [
|
||||
"h1:QFFZshAvwr9L5TQmsNQC6/sDqokk5pjbP8Ae4BQqMLQ=",
|
||||
"zh:2ee426ef3389022db0026792fdc4f2980dcf2600e31adf5a31b4bddfa8d68343",
|
||||
"zh:2f993edb23df55dc1c18150fa187d80aa7d87e6439698ee34b6a6aad23ac2dd7",
|
||||
"zh:3d6601333975e55979b1b454e50ff9a482ce4e0269dd6c72a50202163a8f4463",
|
||||
"zh:4e5f48dce22f7a6d618018d65d1d443bb718defa23f514d5c6385860541fbe79",
|
||||
"zh:5ebf5aea960fc30de381ffd6db20876d249673cf938fe67f1dfb6b9caa1db418",
|
||||
"zh:80ed3fb901141f53b4b56ddb7eea5f2e0c0830d501387539d2c2b8e0cc7e587a",
|
||||
"zh:890df766e9b839623b1f0437355032a3c006226a6c200cd911e15ee1a9014e9f",
|
||||
"zh:9aeae8b3be4a577ced46987fd9159262c5b4c54a510f66592fbcdb40fef55b10",
|
||||
"zh:a0479ef2d308c4a7894f1fe77467cd07e04c7b40d281088f4f204af1bdf94ac6",
|
||||
"zh:a2bdc0c25130665af0b9559942b9813a1ba4889513e7185d4abc9c02e9bb99bd",
|
||||
"zh:b10be9755fe80395ced6f0bbda38b8c8681714cf1eca1d895be239c75c2ffc2a",
|
||||
"zh:ba3d55e722d9f48646574ce7c448f0084fe21fa884b5f8b6d6146a82a99c4baa",
|
||||
"zh:ec1fd0ecaedc787a77d5342b51ae8dea8362a67f1e19123f6521a0e8e012d9e8",
|
||||
"zh:ed49590e69faef14550179f965b4451b31415b8f6be6d33427ad48f65c76b6cf",
|
||||
"zh:f4baa3a2dac719ad20dcfa525bc3f737ad95650b8d0de0c648dc9a87f993b2c3",
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
terraform {
|
||||
backend "pg" {}
|
||||
required_version = "~> 1.7"
|
||||
|
||||
required_providers {
|
||||
cloudflare = {
|
||||
source = "cloudflare/cloudflare"
|
||||
version = "4.46.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
locals {
|
||||
app_name = "ui"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
module "static_pages" {
|
||||
source = "git::https://github.com/immich-app/devtools.git//tf/shared/modules/cloudflare-pages?ref=feat/pages-project-tf-module"
|
||||
|
||||
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.pages_project.outputs.pages_project
|
||||
|
||||
app_name = local.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
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
data "terraform_remote_state" "api_keys_state" {
|
||||
backend = "pg"
|
||||
|
||||
config = {
|
||||
conn_str = var.tf_state_postgres_conn_str
|
||||
schema_name = "prod_cloudflare_api_keys"
|
||||
}
|
||||
}
|
||||
|
||||
data "terraform_remote_state" "cloudflare_account" {
|
||||
backend = "pg"
|
||||
|
||||
config = {
|
||||
conn_str = var.tf_state_postgres_conn_str
|
||||
schema_name = "prod_cloudflare_account"
|
||||
}
|
||||
}
|
||||
|
||||
data "terraform_remote_state" "pages_project" {
|
||||
backend = "pg"
|
||||
|
||||
config = {
|
||||
conn_str = var.tf_state_postgres_conn_str
|
||||
schema_name = "cloudflare_pages_project_immich_app_ui_${var.env}"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
terraform {
|
||||
source = "."
|
||||
|
||||
extra_arguments custom_vars {
|
||||
commands = get_terraform_commands_that_need_vars()
|
||||
}
|
||||
}
|
||||
|
||||
include {
|
||||
path = find_in_parent_folders("state.hcl")
|
||||
}
|
||||
|
||||
locals {
|
||||
env = get_env("TF_VAR_env")
|
||||
stage = get_env("TF_VAR_stage", "")
|
||||
}
|
||||
|
||||
remote_state {
|
||||
backend = "pg"
|
||||
|
||||
config = {
|
||||
conn_str = get_env("TF_VAR_tf_state_postgres_conn_str")
|
||||
schema_name = "cloudflare_immich_app_ui_${local.env}${local.stage}"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
paths = ["../pages-project"]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
variable "tf_state_postgres_conn_str" {}
|
||||
variable "stage" {
|
||||
default = ""
|
||||
}
|
||||
variable "env" {}
|
||||
@@ -0,0 +1,15 @@
|
||||
locals {
|
||||
tf_state_postgres_conn_str = get_env("TF_VAR_tf_state_postgres_conn_str")
|
||||
}
|
||||
|
||||
remote_state {
|
||||
backend = "pg"
|
||||
|
||||
config = {
|
||||
conn_str = local.tf_state_postgres_conn_str
|
||||
}
|
||||
}
|
||||
|
||||
inputs = {
|
||||
tf_state_postgres_conn_str = local.tf_state_postgres_conn_str
|
||||
}
|
||||
Reference in New Issue
Block a user