github workflows actions相关


Pull-AWS-bills-at-regular-intervals-everyday.yml
name: AWS Monthly Cost Report
on:
schedule:
- cron: '0 1 * * *'
workflow_dispatch:
jobs:
report:
runs-on: ubuntu-latest
env:
AWS_REGION: us-east-1
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Configure AWS
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_BILL_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_BILL_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Install jq
run: sudo apt-get update && sudo apt-get install jq -y
- name: Generate Cost Report
run: |
TODAY=$(date +%Y-%m-%d)
FIRST_DAY=$(date +%Y-%m-01)
LAST_DAY=$(date -d "$(date +%Y-%m-01) +1 month -1 day" +%Y-%m-%d)
TOMORROW=$(date -d "+1 day" +%Y-%m-%d)
echo "Current execution time (UTC): $(date)"
# 获取当月已使用金额(1号到今天)
CURRENT=$(aws ce get-cost-and-usage \
--time-period Start=$FIRST_DAY,End=$TODAY \
--granularity MONTHLY \
--metrics "UnblendedCost" \
--query "ResultsByTime[0].Total.UnblendedCost.Amount" \
--output text 2>/dev/null || echo "0")
# 获取整月预测金额(从明天到月底,AWS 会返回整个月的预测)
FORECAST_JSON=$(aws ce get-cost-forecast \
--time-period Start=$TOMORROW,End=$LAST_DAY \
--metric "UNBLENDED_COST" \
--granularity "MONTHLY" \
--output json 2>/dev/null || echo '{"Total":{"Amount":"0"}}')
# 提取 Total.Amount
FORECAST=$(echo "$FORECAST_JSON" | jq -r '.Total.Amount // "0"')
# 如果预测失败,用已使用金额
if [ "$FORECAST" == "None" ] || [ -z "$FORECAST" ] || [ "$FORECAST" == "0" ]; then
FORECAST=$CURRENT
fi
# 获取服务明细
SERVICES=$(aws ce get-cost-and-usage \
--time-period Start=$FIRST_DAY,End=$TODAY \
--granularity MONTHLY \
--metrics "UnblendedCost" \
--group-by Type=DIMENSION,Key=SERVICE 2>/dev/null || echo '{"ResultsByTime":[{"Groups":[]}]}')
SERVICE_LIST=$(echo "$SERVICES" | jq -r '
.ResultsByTime[0].Groups // []
| map(select(.Metrics.UnblendedCost.Amount | tonumber > 1))
| sort_by(.Metrics.UnblendedCost.Amount | tonumber)
| reverse
| .[:10]
| .[]
| "• \(.Keys[0]): $\(.Metrics.UnblendedCost.Amount | tonumber | floor)"
')
if [ -z "$SERVICE_LIST" ]; then
SERVICE_LIST="暂无详细数据"
fi
# 已使用和预测都保留两位小数
CURRENT_FMT=$(echo "$CURRENT" | awk '{printf "%.2f", $1}')
FORECAST_FMT=$(echo "$FORECAST" | awk '{printf "%.2f", $1}')
MESSAGE=$(printf "🧾 AWS 账单播报(%s月)\n\n📊 当月已使用(1号至今): $%s\n🔮 当月预测总额: $%s\n\n🛠️ 服务明细(Top 10):\n%s" "$(date +%m)" "$CURRENT_FMT" "$FORECAST_FMT" "$SERVICE_LIST")
JSON_PAYLOAD=$(jq -n --arg text "$MESSAGE" '{"msg_type": "text", "content": {"text": $text}}')
curl -X POST "${{ secrets.AWS_BILL_WEBHOOK_URL }}" \
-H "Content-Type: application/json" \
-d "$JSON_PAYLOAD"
deploy-aliyun-oss.yaml
name: Deploy to OSS
on:
push:
branches: [main, test]
pull_request:
branches: [main, test]
types: [opened, synchronize, reopened]
workflow_dispatch:
inputs:
environment:
description: '部署环境'
type: choice
options:
- test
- prod
default: 'prod'
env:
NODE_VERSION: '20.12.2'
OSS_REGION: oss-cn-hangzhou
# 飞书通知渠道 (1=生产,通知到前端群, 2=测试,通话到自己的测试群)
FEISHU_CHANNEL: '2'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine Environment
id: env
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "ENV=${{ github.event.inputs.environment }}" >> $GITHUB_ENV
echo "DEPLOY=true" >> $GITHUB_ENV
elif [ "${{ github.event_name }}" == "pull_request" ]; then
if [ "${{ github.base_ref }}" == "main" ]; then
echo "ENV=prod" >> $GITHUB_ENV
else
echo "ENV=test" >> $GITHUB_ENV
fi
echo "DEPLOY=false" >> $GITHUB_ENV
elif [ "${{ github.ref_name }}" == "main" ]; then
echo "ENV=prod" >> $GITHUB_ENV
echo "DEPLOY=true" >> $GITHUB_ENV
else
echo "ENV=test" >> $GITHUB_ENV
echo "DEPLOY=true" >> $GITHUB_ENV
fi
- name: Set Environment Variables
run: |
if [ "$ENV" == "prod" ]; then
echo "OSS_BUCKET=pro-hive" >> $GITHUB_ENV
echo "API_DOMAIN=mainapi.yisuitec.com" >> $GITHUB_ENV
echo "ENV_NAME=生产环境" >> $GITHUB_ENV
echo "CDN_DOMAIN=bit.yisuitec.com" >> $GITHUB_ENV
else
echo "OSS_BUCKET=test-hives" >> $GITHUB_ENV
echo "API_DOMAIN=testapi.yisuitec.com" >> $GITHUB_ENV
echo "ENV_NAME=测试环境" >> $GITHUB_ENV
echo "CDN_DOMAIN=bittest.yisuitec.com" >> $GITHUB_ENV
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Prepare Environment File
run: |
cp .env.example .env
if [ "$ENV" == "prod" ]; then
sed -i 's|http://localhost:8000/api/v1|https://mainapi.yisuitec.com/api/v1 |g' .env
else
sed -i 's|http://localhost:8000/api/v1|https://testapi.yisuitec.com/api/v1 |g' .env
fi
- name: Build
run: |
npm ci
npm run build
- name: Setup ossutil
if: env.DEPLOY == 'true'
uses: manyuanrong/setup-ossutil@v3.0
with:
endpoint: ${{ env.OSS_REGION }}.aliyuncs.com
access-key-id: ${{ secrets.ALIYUN_OSS_ACCESS_KEY_ID }}
access-key-secret: ${{ secrets.ALIYUN_OSS_ACCESS_KEY_SECRET }}
- name: Deploy to OSS
if: env.DEPLOY == 'true'
run: |
ossutil cp -r -f ./dist oss://${{ env.OSS_BUCKET }}/
# ==================== 新增:CDN 刷新 ====================
- name: Install aliyun-cli
if: env.DEPLOY == 'true'
run: |
curl -L -o aliyun-cli-linux-latest-amd64.tgz https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz
tar -xzf aliyun-cli-linux-latest-amd64.tgz
sudo mv aliyun /usr/local/bin/
aliyun --version
- name: Configure aliyun-cli
if: env.DEPLOY == 'true'
run: |
aliyun configure set \
--access-key-id "${{ secrets.ALIYUN_OSS_ACCESS_KEY_ID }}" \
--access-key-secret "${{ secrets.ALIYUN_OSS_ACCESS_KEY_SECRET }}" \
--region "${{ env.OSS_REGION }}"
- name: Refresh CDN Cache
if: env.DEPLOY == 'true'
run: |
echo "正在刷新 CDN 缓存: ${{ env.CDN_DOMAIN }}"
aliyun cdn RefreshObjectCaches \
--ObjectPath "https://${{ env.CDN_DOMAIN }}/" \
--ObjectType Directory
# =====================================================
- name: Send Feishu Notification
if: always()
env:
WEBHOOK_URL_TEST: ${{ secrets.FEISHU_WEBHOOK_URL_TEST }}
WEBHOOK_URL_PROD: ${{ secrets.FEISHU_WEBHOOK_URL }}
run: |
if [ "$FEISHU_CHANNEL" == "1" ]; then
WEBHOOK_URL="$WEBHOOK_URL_PROD"
else
WEBHOOK_URL="$WEBHOOK_URL_TEST"
fi
STATUS="${{ job.status }}"
IS_PR="${{ github.event_name == 'pull_request' }}"
if [ "$IS_PR" == "true" ]; then
if [ "$STATUS" == "success" ]; then
EMOJI="📦"
TITLE="PR 构建成功(预览模式,未部署)"
else
EMOJI="❌"
TITLE="PR 构建失败"
fi
COMMIT_MSG="${{ github.event.pull_request.title }}"
COMMIT_AUTHOR="${{ github.event.pull_request.user.login }}"
COMMIT_HASH="${{ github.event.pull_request.head.sha }}"
BRANCH="${{ github.head_ref }} → ${{ github.base_ref }}"
INVALIDATION_LINE="无"
else
if [ "$STATUS" == "success" ]; then
EMOJI="✅"
TITLE="${{ env.ENV_NAME }} 部署成功"
else
EMOJI="❌"
TITLE="${{ env.ENV_NAME }} 部署失败"
fi
COMMIT_MSG="${{ github.event.head_commit.message || 'manual trigger' }}"
COMMIT_AUTHOR="${{ github.actor }}"
COMMIT_HASH="${{ github.sha }}"
BRANCH="${{ github.ref_name }}"
INVALIDATION_LINE="CDN 已刷新"
fi
COMMIT_MSG=$(echo "$COMMIT_MSG" | tr '\n' ' ' | cut -c1-100)
MESSAGE=$(printf "%s %s\n项目:%s\n编号:#%s\n分支:%s\n提交信息:%s\n作者:%s\n版本:%s\n目标:%s\n失效:%s\n详情:https://github.com/%s/actions/runs/%s " \
"$EMOJI" "$TITLE" \
"${{ github.repository }}" \
"${{ github.run_number }}" \
"$BRANCH" \
"$COMMIT_MSG" \
"$COMMIT_AUTHOR" \
"${COMMIT_HASH:0:7}" \
"${{ env.OSS_BUCKET }}" \
"$INVALIDATION_LINE" \
"${{ github.repository }}" \
"${{ github.run_id }}")
curl -X POST "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "$(jq -n --arg text "$MESSAGE" '{msg_type:"text",content:{text:$text}}')"
deploy-prod.yml
name: Deploy to S3
on:
push:
branches: [main, test]
pull_request:
branches: [main, test]
types: [opened, synchronize, reopened]
workflow_dispatch:
inputs:
environment:
description: '部署环境'
type: choice
options:
- test
- prod
default: 'test'
env:
NODE_VERSION: '20.12.2'
AWS_REGION: ap-northeast-1
# 飞书通知渠道 (1=生产,通知到前端群, 2=测试,通话到自己的测试群)
FEISHU_CHANNEL: '1'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine Environment
id: env
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "ENV=${{ github.event.inputs.environment }}" >> $GITHUB_ENV
echo "DEPLOY=true" >> $GITHUB_ENV
elif [ "${{ github.event_name }}" == "pull_request" ]; then
if [ "${{ github.base_ref }}" == "main" ]; then
echo "ENV=prod" >> $GITHUB_ENV
else
echo "ENV=test" >> $GITHUB_ENV
fi
echo "DEPLOY=false" >> $GITHUB_ENV
elif [ "${{ github.ref_name }}" == "main" ]; then
echo "ENV=prod" >> $GITHUB_ENV
echo "DEPLOY=true" >> $GITHUB_ENV
else
echo "ENV=test" >> $GITHUB_ENV
echo "DEPLOY=true" >> $GITHUB_ENV
fi
- name: Set Environment Variables
run: |
if [ "$ENV" == "prod" ]; then
echo "S3_BUCKET=bit.yisuitec.com" >> $GITHUB_ENV
echo "API_DOMAIN=mainapi.yisuitec.com" >> $GITHUB_ENV
echo "ENV_NAME=生产环境" >> $GITHUB_ENV
echo "CLOUDFRONT_DIST_ID=${{ secrets.CLOUDFRONT_DIST_ID_PROD }}" >> $GITHUB_ENV
else
echo "S3_BUCKET=bittest.yisuitec.com" >> $GITHUB_ENV
echo "API_DOMAIN=testapi.yisuitec.com" >> $GITHUB_ENV
echo "ENV_NAME=测试环境" >> $GITHUB_ENV
echo "CLOUDFRONT_DIST_ID=${{ secrets.CLOUDFRONT_DIST_ID_TEST }}" >> $GITHUB_ENV
fi
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Prepare Environment File
run: |
cp .env.example .env
if [ "$ENV" == "prod" ]; then
sed -i 's|http://localhost:8000/api/v1|https://mainapi.yisuitec.com/api/v1 |g' .env
else
sed -i 's|http://localhost:8000/api/v1|https://testapi.yisuitec.com/api/v1 |g' .env
fi
- name: Build
run: |
npm ci
npm run build
- name: Configure AWS credentials
if: env.DEPLOY == 'true'
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Deploy to S3
if: env.DEPLOY == 'true'
run: |
aws s3 sync ./dist s3://${{ env.S3_BUCKET }}/ --delete
- name: Invalidate CloudFront
if: env.DEPLOY == 'true'
id: invalidate
run: |
INVALIDATION_ID=$(aws cloudfront create-invalidation \
--distribution-id ${{ env.CLOUDFRONT_DIST_ID }} \
--paths "/*" \
--query 'Invalidation.Id' \
--output text)
echo "invalidation_id=$INVALIDATION_ID" >> $GITHUB_OUTPUT
- name: Send Feishu Notification
if: always()
env:
WEBHOOK_URL_TEST: ${{ secrets.FEISHU_WEBHOOK_URL_TEST }}
WEBHOOK_URL_PROD: ${{ secrets.FEISHU_WEBHOOK_URL }}
run: |
# 根据渠道选择 webhook
if [ "$FEISHU_CHANNEL" == "1" ]; then
WEBHOOK_URL="$WEBHOOK_URL_PROD"
else
WEBHOOK_URL="$WEBHOOK_URL_TEST"
fi
STATUS="${{ job.status }}"
IS_PR="${{ github.event_name == 'pull_request' }}"
if [ "$IS_PR" == "true" ]; then
if [ "$STATUS" == "success" ]; then
EMOJI="📦"
TITLE="PR 构建成功(预览模式,未部署)"
else
EMOJI="❌"
TITLE="PR 构建失败"
fi
COMMIT_MSG="${{ github.event.pull_request.title }}"
COMMIT_AUTHOR="${{ github.event.pull_request.user.login }}"
COMMIT_HASH="${{ github.event.pull_request.head.sha }}"
BRANCH="${{ github.head_ref }} → ${{ github.base_ref }}"
INVALIDATION_LINE="无"
else
if [ "$STATUS" == "success" ]; then
EMOJI="✅"
TITLE="${{ env.ENV_NAME }} 部署成功"
else
EMOJI="❌"
TITLE="${{ env.ENV_NAME }} 部署失败"
fi
COMMIT_MSG="${{ github.event.head_commit.message || 'manual trigger' }}"
COMMIT_AUTHOR="${{ github.actor }}"
COMMIT_HASH="${{ github.sha }}"
BRANCH="${{ github.ref_name }}"
INVALIDATION_LINE="${{ steps.invalidate.outputs.invalidation_id || '无' }}"
fi
COMMIT_MSG=$(echo "$COMMIT_MSG" | tr '\n' ' ' | cut -c1-100)
# 使用 printf 生成带换行的消息
MESSAGE=$(printf "%s %s\n项目:%s\n编号:#%s\n分支:%s\n提交信息:%s\n作者:%s\n版本:%s\n目标:%s\n失效:%s\n详情:https://github.com/%s/actions/runs/%s " \
"$EMOJI" "$TITLE" \
"${{ github.repository }}" \
"${{ github.run_number }}" \
"$BRANCH" \
"$COMMIT_MSG" \
"$COMMIT_AUTHOR" \
"${COMMIT_HASH:0:7}" \
"${{ env.S3_BUCKET }}" \
"$INVALIDATION_LINE" \
"${{ github.repository }}" \
"${{ github.run_id }}")
curl -X POST "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "$(jq -n --arg text "$MESSAGE" '{msg_type:"text",content:{text:$text}}')"
get_tencentcloud_sms.py
# -*- coding: utf-8 -*-
import os
import json
import sys
from datetime import datetime, timedelta
import requests
from tencentcloud.common import credential
from tencentcloud.common.profile.client_profile import ClientProfile
from tencentcloud.common.profile.http_profile import HttpProfile
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.sms.v20210111 import sms_client, models
def send_feishu_message(webhook_url: str, content: dict):
"""发送飞书富文本卡片消息"""
headers = {"Content-Type": "application/json; charset=utf-8"}
data = {
"msg_type": "interactive",
"card": {
"config": {"wide_screen_mode": True},
"header": {
"title": {"tag": "plain_text", "content": "📱 腾讯云短信余量提醒"},
"template": "blue"
},
"elements": [
{
"tag": "div",
"text": {
"tag": "lark_md",
"content": f"**剩余条数:{content['remaining']} 条**\n\n"
f"总量:{content['total']} 条\n"
f"已用:{content['used']} 条\n"
f"有效期至:{content['expired']}"
}
},
{"tag": "hr"},
{
"tag": "note",
"elements": [
{"tag": "plain_text", "content": f"查询时间:{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"}
]
}
]
}
}
try:
resp = requests.post(webhook_url, headers=headers, json=data, timeout=10)
if resp.status_code == 200:
print("✅ 飞书消息发送成功")
else:
print(f"❌ 飞书发送失败: {resp.status_code} - {resp.text}", file=sys.stderr)
except Exception as e:
print(f"❌ 飞书请求异常: {e}", file=sys.stderr)
def main():
# 从环境变量直接读取(适配 GitHub Actions)
secret_id = os.getenv("TENCENTCLOUD_SECRET_ID")
secret_key = os.getenv("TENCENTCLOUD_SECRET_KEY")
sdk_app_id = os.getenv("TENCENT_SMS_SDK_APP_ID")
feishu_webhook = os.getenv("FEISHU_SMS_WEBHOOK_URL")
if not all([secret_id, secret_key, sdk_app_id]):
print("❌ 缺少必要环境变量: TENCENTCLOUD_SECRET_ID, TENCENTCLOUD_SECRET_KEY, TENCENT_SMS_SDK_APP_ID", file=sys.stderr)
sys.exit(1)
try:
cred = credential.Credential(secret_id, secret_key)
httpProfile = HttpProfile()
httpProfile.endpoint = "sms.tencentcloudapi.com"
clientProfile = ClientProfile()
clientProfile.httpProfile = httpProfile
client = sms_client.SmsClient(cred, "ap-guangzhou", clientProfile)
end_time = datetime.now()
begin_time = end_time - timedelta(days=730)
params = {
"SmsSdkAppId": sdk_app_id,
"Limit": 100,
"Offset": 0,
"BeginTime": begin_time.strftime("%Y%m%d%H"),
"EndTime": end_time.strftime("%Y%m%d%H")
}
req = models.SmsPackagesStatisticsRequest()
req.from_json_string(json.dumps(params))
resp = client.SmsPackagesStatistics(req)
# 输出原始响应(和你示例一致)
result_str = resp.to_json_string()
print("✅ 查询成功!")
print(result_str)
# 解析并发送飞书
data = json.loads(result_str)
packages = data.get("SmsPackagesStatisticsSet", [])
if packages:
pkg = packages[0]
total = pkg["PackageAmount"]
used = pkg["CurrentUsage"]
remaining = total - used
expired_str = datetime.fromtimestamp(pkg["PackageExpiredTime"]).strftime("%Y-%m-%d")
if feishu_webhook:
send_feishu_message(feishu_webhook, {
"total": total,
"used": used,
"remaining": remaining,
"expired": expired_str
})
except TencentCloudSDKException as e:
print(f"❌ 腾讯云 API 错误: {e.code} - {e.message}", file=sys.stderr)
sys.exit(1)
except Exception as e:
print(f"❌ 未知错误: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
get_tencentcloud_sms.yaml
name: Check Tencent SMS Balance
on:
workflow_dispatch:
schedule:
- cron: '1 1 * * *'
jobs:
check-balance:
runs-on: ubuntu-latest
env:
TENCENTCLOUD_SECRET_ID: ${{ secrets.TENCENT_SMS_SECRET_ID }}
TENCENTCLOUD_SECRET_KEY: ${{ secrets.TENCENT_SMS_SECRET_KEY }}
TENCENT_SMS_SDK_APP_ID: ${{ secrets.TENCENT_SMS_SDK_APP_ID }}
FEISHU_SMS_WEBHOOK_URL: ${{ secrets.FEISHU_SMS_WEBHOOK_URL }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: |
pip install tencentcloud-sdk-python requests
- name: Run SMS balance check
run: python .github/workflows/get_tencentcloud_sms.py
- name: Send fallback Feishu notification on failure
if: failure()
env:
FEISHU_SMS_WEBHOOK_URL: ${{ secrets.FEISHU_SMS_WEBHOOK_URL }}
run: |
MESSAGE="❌ 腾讯云短信余量检查失败!\n项目:${{ github.repository }}\n工作流:Check SMS Balance\n详情:https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
curl -X POST "$FEISHU_SMS_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "$(jq -n --arg text "$MESSAGE" '{msg_type:"text",content:{text:$text}}')"
本文来自博客园,作者:六月OvO,转载请注明原文链接:https://www.cnblogs.com/chenlifan/p/19729282

浙公网安备 33010602011771号