feat: add in copy of updated shared deployment workflows for now until they are
published
This commit is contained in:
parent
bcb7f86b7f
commit
e245bcbe96
12 changed files with 1113 additions and 0 deletions
240
.github/workflows/build.yml
vendored
Normal file
240
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
# yaml-language-server: $schema=https://json-schema.org/draft-07/schema#
|
||||
name: _build
|
||||
|
||||
concurrency:
|
||||
group: build
|
||||
cancel-in-progress: false
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
|
||||
jobs:
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
outputs:
|
||||
should-build: ${{ steps.check.outputs.should-build }}
|
||||
artifact-run-id: ${{ steps.check.outputs.artifact-run-id }}
|
||||
artifact-id: ${{ steps.check.outputs.artifact-id }}
|
||||
build-sha: ${{ steps.check.outputs.build-sha }}
|
||||
repo-name: ${{ steps.meta.outputs.repo-name }}
|
||||
repo-path: ${{ steps.meta.outputs.repo-path }}
|
||||
image-tar: ${{ steps.check.outputs.image-tar }}
|
||||
prod: ${{ steps.meta.outputs.prod }}
|
||||
tag: ${{ steps.check.outputs.tag }}
|
||||
should-deploy: ${{ steps.check.outputs.should-deploy }}
|
||||
steps:
|
||||
- name: Checkout the repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Determine build metadata
|
||||
id: meta
|
||||
uses: ./.github/actions/determine-build-metadata
|
||||
with:
|
||||
repository: ${{ github.repository }}
|
||||
github-ref: ${{ github.ref }}
|
||||
|
||||
- name: Check if build/deploy needed
|
||||
id: check
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -eu -o pipefail
|
||||
|
||||
SHOULD_BUILD=false
|
||||
SHOULD_DEPLOY=false
|
||||
|
||||
SHORT_SHA="${{ github.sha }}"
|
||||
SHORT_SHA="${SHORT_SHA:0:7}"
|
||||
IMAGE_TAR="${{ steps.meta.outputs.repo-name }}-${{github.sha}}.tar"
|
||||
VERSION="$(git tag --points-at ${{ github.sha }})"
|
||||
IS_VERSION_TAGGED="$([[ "$VERSION" =~ ^v ]] && echo "true" || echo "false")"
|
||||
BRANCH_NAME="$([[ "${{ github.ref }}" =~ ^refs/tags/v ]] && echo "main" || echo "${{ github.ref }}" | sed 's/refs\/heads\///')"
|
||||
COMMIT_MESSAGE="$(git log --format=%s -n 1 ${{ github.sha }})"
|
||||
if [[ "${{ steps.meta.outputs.prod }}" == "true" ]]; then
|
||||
TAG="${{ steps.meta.outputs.repo-name }}:sha-${SHORT_SHA},${{ steps.meta.outputs.repo-name }}:latest"
|
||||
else
|
||||
TAG="${{ steps.meta.outputs.repo-name }}-dev:sha-${SHORT_SHA}"
|
||||
fi
|
||||
|
||||
if [[ "$BRANCH_NAME" == "staging" ]]; then
|
||||
echo "🔨 Build is on staging - will also deploy to staging"
|
||||
SHOULD_DEPLOY="true"
|
||||
elif [[ "$BRANCH_NAME" == "main" ]]; then
|
||||
if [[ ! "$COMMIT_MESSAGE" =~ ^chore:\ Release ]] || [[ "$IS_VERSION_TAGGED" == "false" ]]; then
|
||||
echo "❌ Skipping build for main: $COMMIT_MESSAGE. Not a version release. Missing tag or proper commit message for release."
|
||||
exit 1
|
||||
fi
|
||||
echo "🏷️ Version tagged build on main: $VERSION - will also deploy to production"
|
||||
SHOULD_DEPLOY="true"
|
||||
fi
|
||||
|
||||
echo "🔍 Checking for in progress builds with git SHA ${{github.sha}}"
|
||||
IN_PROGRESS_BUILDS=""
|
||||
if ! IN_PROGRESS_BUILDS=$(gh api repos/${{github.repository}}/actions/runs --jq '.workflow_runs[] | select(.head_sha == "${{github.sha}}" and .status == "in_progress" and .name == "Build/Deploy" and .id != ${{ github.run_id }}) | .id' 2>/dev/null); then
|
||||
echo "❌ Failed to check for in-progress builds via GitHub API"
|
||||
echo "::error::GitHub API call failed while checking for in-progress builds"
|
||||
exit 1
|
||||
fi
|
||||
IN_PROGRESS_BUILDS=$(echo "$IN_PROGRESS_BUILDS" | head -1)
|
||||
|
||||
if [[ -n "$IN_PROGRESS_BUILDS" ]]; then
|
||||
echo "❌ Found in-progress build for SHA ${{github.sha}}: $IN_PROGRESS_BUILDS - wait for existing build to complete before triggering a new one or rerunning this workflow"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "🔍 Checking for existing build artifacts with git SHA ${{github.sha}}"
|
||||
|
||||
# Check for existing artifacts with this SHA across all workflow runs
|
||||
ARTIFACT_INFO=""
|
||||
if ! ARTIFACT_INFO=$(gh api repos/${{github.repository}}/actions/artifacts --jq '.artifacts[] | select(.name | endswith("-${{github.sha}}.tar")) | {id, name, workflow_run} | @json' 2>/dev/null); then
|
||||
echo "❌ Failed to check for existing artifacts via GitHub API"
|
||||
echo "::error::GitHub API call failed while checking for existing artifacts"
|
||||
exit 1
|
||||
fi
|
||||
ARTIFACT_INFO=$(echo "$ARTIFACT_INFO" | head -1)
|
||||
|
||||
if [[ -n "$ARTIFACT_INFO" ]]; then
|
||||
ARTIFACT_ID=$(echo "$ARTIFACT_INFO" | jq -r '.id')
|
||||
ARTIFACT_NAME=$(echo "$ARTIFACT_INFO" | jq -r '.name')
|
||||
RUN_ID=$(echo "$ARTIFACT_INFO" | jq -r '.workflow_run.id')
|
||||
|
||||
echo "✅ Found existing artifact $ARTIFACT_NAME from run: $RUN_ID"
|
||||
echo "artifact-id=$ARTIFACT_ID" >> $GITHUB_OUTPUT
|
||||
echo "artifact-id -> $ARTIFACT_ID"
|
||||
else
|
||||
RUN_ID="${{ github.run_id }}"
|
||||
SHOULD_BUILD="true"
|
||||
echo "🔨 No existing build artifact found; will build..."
|
||||
fi
|
||||
|
||||
echo "should-build=$SHOULD_BUILD" >> $GITHUB_OUTPUT
|
||||
echo "should-build -> $SHOULD_BUILD"
|
||||
echo "should-deploy=$SHOULD_DEPLOY" >> $GITHUB_OUTPUT
|
||||
echo "should-deploy -> $SHOULD_DEPLOY"
|
||||
echo "artifact-run-id=$RUN_ID" >> $GITHUB_OUTPUT
|
||||
echo "artifact-run-id -> $RUN_ID"
|
||||
echo "build-sha=${{github.sha}}" >> $GITHUB_OUTPUT
|
||||
echo "build-sha -> ${{github.sha}}"
|
||||
echo "image-tar=$IMAGE_TAR" >> $GITHUB_OUTPUT
|
||||
echo "image-tar -> $IMAGE_TAR"
|
||||
echo "tag=$TAG" >> $GITHUB_OUTPUT
|
||||
echo "tag -> $TAG"
|
||||
|
||||
build:
|
||||
needs: check
|
||||
if: ${{ needs.check.outputs.should-build == 'true' }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
artifact-id: ${{ steps.upload-artifact.outputs.artifact-id }}
|
||||
steps:
|
||||
- name: Checkout the repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Ensure scripts are executable
|
||||
run: chmod +x deploy/scripts/*.sh
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Generate cache keys
|
||||
id: cache-keys
|
||||
run: |
|
||||
# Generate cache key based on package-lock.json for dependencies
|
||||
DEPS_HASH=$(sha256sum package-lock.json | cut -d' ' -f1 | cut -c1-8)
|
||||
echo "deps-hash=${DEPS_HASH}" >> $GITHUB_OUTPUT
|
||||
echo "deps-hash -> $DEPS_HASH"
|
||||
|
||||
# Generate cache key based on source files and paths
|
||||
SOURCE_PATHS="app/ server/ public/ nuxt.config.ts tsconfig.json tailwind.config.js package.json"
|
||||
SRC_HASH=$(find $SOURCE_PATHS -type f 2>/dev/null -exec sha256sum {} + | sha256sum | cut -d' ' -f1 | cut -c1-8 || echo "fallback-$(date +%s)")
|
||||
echo "src-hash=${SRC_HASH}" >> $GITHUB_OUTPUT
|
||||
echo "src-hash -> $SRC_HASH"
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-deps-${{ steps.cache-keys.outputs.deps-hash }}-src-${{ steps.cache-keys.outputs.src-hash }}-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-deps-${{ steps.cache-keys.outputs.deps-hash }}-src-${{ steps.cache-keys.outputs.src-hash }}-
|
||||
${{ runner.os }}-buildx-deps-${{ steps.cache-keys.outputs.deps-hash }}-
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Extract version for Docker build
|
||||
id: extract_version
|
||||
run: |
|
||||
if [[ "${{ github.ref }}" =~ ^refs/tags/v([0-9]+\.[0-9]+\.[0-9]+)(-.*)?$ ]]; then
|
||||
VERSION="${BASH_REMATCH[1]}"
|
||||
if [[ -n "${BASH_REMATCH[2]}" ]]; then
|
||||
VERSION="${VERSION}${BASH_REMATCH[2]}"
|
||||
fi
|
||||
echo "🏷️ Using git tag version: ${VERSION}"
|
||||
else
|
||||
VERSION=$(node -p "require('./package.json').version || '0.0.0'")
|
||||
SHA="${{ github.sha }}"
|
||||
GIT_SHA_SHORT="${SHA:0:7}"
|
||||
VERSION="${VERSION}+${GIT_SHA_SHORT}"
|
||||
echo "📦 Using package.json + SHA version: ${VERSION}"
|
||||
fi
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build container
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
outputs: type=docker,dest=${{ runner.temp }}/${{ needs.check.outputs.image-tar }}
|
||||
tags: ${{ needs.check.outputs.tag }}
|
||||
build-args: |
|
||||
VERSION=${{ env.VERSION }}
|
||||
context: .
|
||||
target: runtime
|
||||
cache-from: type=local,src=/tmp/.buildx-cache
|
||||
cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max
|
||||
platforms: linux/amd64
|
||||
|
||||
- name: Rotate cache
|
||||
run: |
|
||||
rm -rf /tmp/.buildx-cache
|
||||
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
|
||||
|
||||
- name: Upload container as artifact
|
||||
id: upload-artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ needs.check.outputs.image-tar }}
|
||||
path: ${{ runner.temp }}/${{ needs.check.outputs.image-tar }}
|
||||
if-no-files-found: error
|
||||
retention-days: 30
|
||||
compression-level: 0
|
||||
|
||||
- name: Notify successful build
|
||||
run: |
|
||||
echo "🎉 Build completed successfully!"
|
||||
echo "📋 Summary:"
|
||||
echo " - Source: ${{ github.ref }}"
|
||||
echo " - Status: ✅ Build Successful"
|
||||
echo " - Next: Stage this build manually or wait for the next scheduled staging."
|
||||
|
||||
deploy:
|
||||
needs: [check, build]
|
||||
if: ${{ always() && needs.check.outputs.should-deploy == 'true' }}
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
uses: ./.github/workflows/deploy.yml
|
||||
with:
|
||||
artifact-run-id: ${{ needs.check.outputs.artifact-run-id }}
|
||||
artifact-id: ${{ needs.check.outputs.should-build == 'true' && needs.build.outputs.artifact-id || needs.check.outputs.artifact-id }}
|
||||
build-sha: ${{ needs.check.outputs.build-sha }}
|
||||
repo-name: ${{ needs.check.outputs.repo-name }}
|
||||
repo-path: ${{ needs.check.outputs.repo-path }}
|
||||
image-tar: ${{ needs.check.outputs.image-tar }}
|
||||
prod: ${{ needs.check.outputs.prod == 'true' }}
|
||||
tag: ${{ needs.check.outputs.tag }}
|
||||
secrets: inherit
|
||||
159
.github/workflows/deploy.yml
vendored
Normal file
159
.github/workflows/deploy.yml
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
# yaml-language-server: $schema=https://json-schema.org/draft-07/schema#
|
||||
name: _deploy
|
||||
|
||||
concurrency:
|
||||
group: deploy
|
||||
cancel-in-progress: false
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
tag:
|
||||
description: "Tag to deploy"
|
||||
required: true
|
||||
type: string
|
||||
prod:
|
||||
description: "Whether to deploy to production"
|
||||
required: true
|
||||
type: boolean
|
||||
image-tar:
|
||||
description: "Name of image tarball"
|
||||
required: true
|
||||
type: string
|
||||
artifact-run-id:
|
||||
description: "ID of workflow run where artifact was created"
|
||||
required: true
|
||||
type: string
|
||||
artifact-id:
|
||||
description: "ID of artifact"
|
||||
required: true
|
||||
type: string
|
||||
build-sha:
|
||||
description: "SHA of build"
|
||||
required: true
|
||||
type: string
|
||||
repo-name:
|
||||
description: "Name of repository"
|
||||
required: true
|
||||
type: string
|
||||
repo-path:
|
||||
description: "Path to repository on server"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
environment: ${{ inputs.prod && 'production' || 'staging' }}
|
||||
env:
|
||||
RELEASE_TYPE: ${{ inputs.prod && 'prod' || 'staging' }}
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
steps:
|
||||
- name: Checkout the repo
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Ensure scripts are executable
|
||||
run: chmod +x deploy/scripts/*.sh
|
||||
|
||||
- name: Setup environment configuration
|
||||
uses: ./.github/actions/setup-environment
|
||||
with:
|
||||
release-type: ${{ inputs.prod && 'prod' || 'staging' }}
|
||||
domain: ${{ secrets.DOMAIN }}
|
||||
android-sms-gateway-url: ${{ secrets.NUXT_ANDROID_SMS_GATEWAY_URL }}
|
||||
android-sms-gateway-login: ${{ secrets.NUXT_ANDROID_SMS_GATEWAY_LOGIN }}
|
||||
android-sms-gateway-password: ${{ secrets.NUXT_ANDROID_SMS_GATEWAY_PASSWORD }}
|
||||
my-phone-number: ${{ secrets.NUXT_MY_PHONE_NUMBER }}
|
||||
super-secret-salt: ${{ secrets.NUXT_SUPER_SECRET_SALT }}
|
||||
wireguard-allowed-ips: ${{ secrets.WIREGUARD_ALLOWED_IPS }}
|
||||
wireguard-private-key: ${{ secrets.WIREGUARD_PRIVATE_KEY }}
|
||||
wireguard-addresses: ${{ secrets.WIREGUARD_ADDRESSES }}
|
||||
wireguard-public-key: ${{ secrets.WIREGUARD_PUBLIC_KEY }}
|
||||
wireguard-endpoint-host: ${{ secrets.WIREGUARD_ENDPOINT_HOST }}
|
||||
wireguard-endpoint-port: ${{ secrets.WIREGUARD_ENDPOINT_PORT }}
|
||||
prod: ${{ inputs.prod }}
|
||||
|
||||
- name: Set up SSH
|
||||
run: |
|
||||
mkdir -p $HOME/.ssh
|
||||
echo -e "${{ secrets.DEPLOY_KEY }}" > $HOME/.ssh/deploy.key
|
||||
chmod 700 $HOME/.ssh
|
||||
chmod 600 $HOME/.ssh/deploy.key
|
||||
|
||||
cat >>$HOME/.ssh/config <<END
|
||||
Host deploy
|
||||
HostName ${{ secrets.DEPLOY_HOST }}
|
||||
Port ${{ secrets.DEPLOY_PORT }}
|
||||
User ${{ secrets.DEPLOY_USER }}
|
||||
IdentityFile $HOME/.ssh/deploy.key
|
||||
UserKnownHostsFile /dev/null
|
||||
StrictHostKeyChecking no
|
||||
ControlMaster auto
|
||||
ControlPath $HOME/.ssh/control-%C
|
||||
ControlPersist yes
|
||||
LogLevel QUIET
|
||||
ConnectionAttempts 3
|
||||
ConnectTimeout 10
|
||||
ServerAliveInterval 10
|
||||
END
|
||||
|
||||
- name: Download container image artifact
|
||||
id: download-artifact
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: ${{ runner.temp }}
|
||||
github-token: ${{ github.token }}
|
||||
artifact-ids: ${{ inputs.artifact-id }}
|
||||
run-id: ${{ inputs.artifact-run-id }}
|
||||
|
||||
- name: Configure HAProxy
|
||||
env:
|
||||
DOCKER_HOST: ssh://deploy
|
||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
DOMAIN: ${{ secrets.DOMAIN }}
|
||||
BACKEND_NAME: ${{ inputs.repo-name }}-${{ env.RELEASE_TYPE }}
|
||||
CF_PEM_CERT: ${{ secrets.CF_PEM_CERT }}
|
||||
CF_PEM_CA: ${{ secrets.CF_PEM_CA }}
|
||||
run: |
|
||||
echo "✅ Exit on any error"
|
||||
set -eu -o pipefail
|
||||
|
||||
echo "🔄 Load environment variables"
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
|
||||
echo "🎯 Running HAProxy configuration script"
|
||||
./deploy/scripts/configure-haproxy.sh add "$BACKEND_NAME"
|
||||
|
||||
- name: Deploy to Server
|
||||
env:
|
||||
IMAGE_TAR: ${{ steps.download-artifact.outputs.download-path }}/${{ inputs.image-tar }}/${{ inputs.image-tar }}
|
||||
REPO_PROJECT_PATH: ${{ inputs.repo-path }}
|
||||
IMAGE_TAG: ${{ inputs.tag }}
|
||||
GIT_SHA: ${{ inputs.build-sha }}
|
||||
DOCKER_HOST: ssh://deploy
|
||||
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
|
||||
run: |
|
||||
echo "✅ Exit on any error"
|
||||
set -eu -o pipefail
|
||||
|
||||
echo "🔄 Load environment variables"
|
||||
set -a
|
||||
source .env
|
||||
set +a
|
||||
|
||||
echo "🎯 Running deployment script"
|
||||
./deploy/scripts/deploy-blue-green.sh
|
||||
|
||||
- name: Notify successful deployment
|
||||
run: |
|
||||
echo "🎉 Deployment completed successfully!"
|
||||
echo "📋 Summary:"
|
||||
echo " - Source: ${{ inputs.prod && 'main (production)' || 'staging' }} branch"
|
||||
echo " - Status: ✅ Deployed Successfully"
|
||||
echo " - Next: Verify deployment status!"
|
||||
162
.github/workflows/release.yml
vendored
Normal file
162
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
name: _release
|
||||
|
||||
#TODO test without these
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: release
|
||||
cancel-in-progress: false
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
force_release:
|
||||
description: "Force release even if validation fails"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
validate-staging-build:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_release: ${{ steps.validation.outputs.should-proceed }}
|
||||
steps:
|
||||
- name: Checkout staging
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: staging
|
||||
|
||||
- name: Validate staging build status
|
||||
id: validation
|
||||
uses: ./.github/actions/validate-build-status
|
||||
with:
|
||||
job-name: trigger-staging-build / build
|
||||
from-branch: staging
|
||||
to-branch: main
|
||||
force: ${{ inputs.force_release }}
|
||||
|
||||
generate-release-and-push-all:
|
||||
needs: validate-staging-build
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ inputs.force_release == true || needs.validate-staging-build.outputs.should_release == 'true' }}
|
||||
outputs:
|
||||
skip-release: ${{steps.semantic-release.outputs.skip-release }}
|
||||
steps:
|
||||
- name: Checkout staging
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: staging
|
||||
|
||||
- name: Setup Git SSH for commit signing and push operations
|
||||
uses: ./.github/actions/setup-git-ssh
|
||||
with:
|
||||
commit-ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
push-ssh-private-key: ${{ secrets.PUSH_SSH_PRIVATE_KEY }}
|
||||
actor: ${{ github.actor }}
|
||||
actor-id: ${{ github.actor_id }}
|
||||
|
||||
- name: Set git origin to GIT_ORIGIN_REPO_URL
|
||||
run: |
|
||||
GIT_ORIGIN_URL="${{ secrets.GIT_ORIGIN_REPO_URL }}"
|
||||
if [ -n "$GIT_ORIGIN_URL" ]; then
|
||||
git remote remove origin
|
||||
if git remote add -f --tags origin "$GIT_ORIGIN_URL"; then
|
||||
echo "✅ Git origin configured correctly"
|
||||
else
|
||||
echo "❌ Failed to configure Git origin"
|
||||
echo "::error::Failed to configure Git origin"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "❌ No Git origin URL configured - cannot proceed without origin URL"
|
||||
echo "::error::GIT_ORIGIN_REPO_URL secret is required but not configured"
|
||||
exit 1
|
||||
fi
|
||||
- name: Merge staging commits to main
|
||||
run: |
|
||||
echo "🔄 Merging staging commits to main..."
|
||||
git checkout main
|
||||
|
||||
STAGING_COMMITS=$(git log --oneline main..staging --pretty=format:"* %s")
|
||||
|
||||
if [ -z "$STAGING_COMMITS" ]; then
|
||||
if [[ "${{inputs.force_release}}" == "true" ]]; then
|
||||
echo "⚠️ No new commits in staging to merge, but force release is enabled"
|
||||
echo "::warning::No commits to merge from staging to main"
|
||||
exit 0
|
||||
else
|
||||
echo "❌ No new commits in staging to merge"
|
||||
echo "::error::No commits to merge from staging to main"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "📋 Commits to be merged:"
|
||||
echo "$STAGING_COMMITS"
|
||||
|
||||
if git merge staging -X theirs -m "chore: merge staging changes for next release"; then
|
||||
echo "✅ Staging commits merged to main successfully"
|
||||
else
|
||||
echo "⚠️ Merge conflicts detected - resolving with staging preference..."
|
||||
|
||||
git checkout staging -- .
|
||||
|
||||
if [ -f CHANGELOG.md ]; then
|
||||
git checkout HEAD -- CHANGELOG.md
|
||||
echo "📋 Kept main's CHANGELOG.md (managed by semantic-release)"
|
||||
fi
|
||||
|
||||
git add -A
|
||||
git commit -m "chore: merge staging changes for next release"
|
||||
echo "✅ Conflicts resolved - staging files preferred, main's CHANGELOG.md kept"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: ".nvmrc"
|
||||
|
||||
- name: Install semantic-release
|
||||
run: npm install semantic-release
|
||||
|
||||
- name: Run semantic-release on main, generate release, and push
|
||||
id: semantic-release
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ github.token }}" # used for release generation
|
||||
GIT_AUTHOR_NAME: "${{ github.actor }}"
|
||||
GIT_AUTHOR_EMAIL: "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
|
||||
GIT_COMMITTER_NAME: "${{ github.actor }}"
|
||||
GIT_COMMITTER_EMAIL: "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
|
||||
run: |
|
||||
echo "🚀 Running semantic-release on main branch..."
|
||||
npx semantic-release
|
||||
|
||||
if git log main -1 --format="%s" | grep -q "^chore: Release"; then
|
||||
echo "✅ Semantic-release completed and pushed to origin/main"
|
||||
echo "skip-release=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "❌ Semantic-release did not generate a release, exiting."
|
||||
echo "skip-release=true" >> $GITHUB_OUTPUT
|
||||
[[ "${{ inputs.force_release }}" == "true" ]] && exit 1
|
||||
fi
|
||||
|
||||
- name: Notify successful release
|
||||
if: ${{ steps.semantic-release.outputs.skip-release != 'true' }}
|
||||
run: |
|
||||
echo "🎉 Production release completed successfully!"
|
||||
echo "📋 Summary:"
|
||||
echo " - Source: staging branch commits (regular merged)"
|
||||
echo " - Target: main branch (semantic-release)"
|
||||
echo " - Semantic-release: ✅ Complete on main"
|
||||
echo " - staging branch: ℹ️ Maintains independent history"
|
||||
echo " - dev branch: ℹ️ Maintains independent history"
|
||||
echo " - Tags: ✅ Created and pushed"
|
||||
echo " - GitHub & Git Origin: ✅ Main branch synchronized"
|
||||
echo " - Status: 🚀 Ready to build and deploy!"
|
||||
37
.github/workflows/scheduler-release.yml
vendored
Normal file
37
.github/workflows/scheduler-release.yml
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
name: Release
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run weekly on Mondays 12:00 UTC/04:00 PST (UTC-08)
|
||||
- cron: "0 12 * * 1"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
force_release:
|
||||
description: "Force release even if validation fails"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
note_for_dispatch:
|
||||
description: "Note: This workflow will always call release.yml@staging, but it MUST BE RUN FROM THE MAIN BRANCH to ensure environment consistency."
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
call-release-workflow:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
issues: write
|
||||
pull-requests: write
|
||||
uses: badbl0cks/portfolio/.github/workflows/release.yml@staging
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
force_release: ${{ inputs.force_release || false }}
|
||||
secrets: inherit
|
||||
33
.github/workflows/scheduler-stage.yml
vendored
Normal file
33
.github/workflows/scheduler-stage.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
name: Stage
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Run daily Monday-Friday at 13:00 UTC/05:00 PST (UTC-08)
|
||||
- cron: "0 13 * * 1-5"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
force_staging:
|
||||
description: "Force staging even if validation fails"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
note_for_dispatch:
|
||||
description: "Note: This workflow will always call stage.yml@dev, but it MUST BE RUN FROM THE MAIN BRANCH to ensure environment consistency."
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
call-staging-workflow:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
uses: badbl0cks/portfolio/.github/workflows/stage.yml@dev
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
force_staging: ${{ inputs.force_staging || false }}
|
||||
secrets: inherit
|
||||
80
.github/workflows/stage.yml
vendored
Normal file
80
.github/workflows/stage.yml
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
name: _stage
|
||||
|
||||
permissions:
|
||||
actions: read
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: stage
|
||||
cancel-in-progress: false
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
force_staging:
|
||||
description: "Force staging even if validation fails"
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
validate-dev-build:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_stage: ${{ steps.validation.outputs.should-proceed }}
|
||||
steps:
|
||||
- name: Checkout dev
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: dev
|
||||
|
||||
- name: Validate dev build status
|
||||
id: validation
|
||||
uses: ./.github/actions/validate-build-status
|
||||
with:
|
||||
job-name: trigger-dev-build / build
|
||||
from-branch: dev
|
||||
to-branch: staging
|
||||
force: ${{ inputs.force_staging }}
|
||||
|
||||
push-to-staging:
|
||||
needs: validate-dev-build
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ inputs.force_staging == true || needs.validate-dev-build.outputs.should_stage == 'true' }}
|
||||
steps:
|
||||
- name: Checkout dev
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: dev
|
||||
|
||||
- name: Setup Git SSH for push operations
|
||||
uses: ./.github/actions/setup-git-ssh
|
||||
with:
|
||||
push-ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
actor: ${{ github.actor }}
|
||||
actor-id: ${{ github.actor_id }}
|
||||
|
||||
- name: Reset staging branch to refs/heads/dev
|
||||
run: |
|
||||
echo "🚀 Resetting staging branch..."
|
||||
|
||||
git checkout staging
|
||||
git reset --hard dev
|
||||
echo "✅ Staging branch reset to refs/heads/dev"
|
||||
|
||||
- name: Push to git origin
|
||||
uses: ./.github/actions/push-to-origin
|
||||
with:
|
||||
git-origin-url: ${{ secrets.GIT_ORIGIN_REPO_URL }}
|
||||
branches: staging
|
||||
force: true
|
||||
|
||||
- name: Notify successful staging
|
||||
run: |
|
||||
echo "🎉 Staging completed successfully!"
|
||||
echo "📋 Summary:"
|
||||
echo " - Source: dev branch"
|
||||
echo " - Status: ✅ Pushed to staging"
|
||||
echo " - Next: Perform staging and integration tests. If satisfactory, manually dispatch the staging-to-main workflow to generate a production release, which will then trigger a production build and deployment."
|
||||
30
.github/workflows/trigger-build.yml
vendored
Normal file
30
.github/workflows/trigger-build.yml
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# yaml-language-server: $schema=https://json-schema.org/draft-07/schema#
|
||||
name: Build/Deploy
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [dev, staging, main]
|
||||
|
||||
jobs:
|
||||
trigger-dev-build:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
if: ${{ github.ref == 'refs/heads/dev' }}
|
||||
uses: badbl0cks/portfolio/.github/workflows/build.yml@dev
|
||||
secrets: inherit
|
||||
trigger-staging-build:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
if: ${{ github.ref == 'refs/heads/staging' }}
|
||||
uses: badbl0cks/portfolio/.github/workflows/build.yml@staging
|
||||
secrets: inherit
|
||||
trigger-main-build:
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
if: ${{ github.ref == 'refs/heads/main' }}
|
||||
uses: badbl0cks/portfolio/.github/workflows/build.yml@main
|
||||
secrets: inherit
|
||||
Loading…
Add table
Add a link
Reference in a new issue