From f9e0a013d883f76feb2cf1091e7974b6acdd3411 Mon Sep 17 00:00:00 2001 From: Lyda <1829913225@qq.com> Date: Thu, 21 Aug 2025 11:58:51 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E7=AE=A1=E7=90=86=20GitHub=20Action=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=8D=E6=93=8D=E4=BD=9C=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=EF=BC=88=E8=8E=B7=E5=8F=96=E3=80=81=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E3=80=81=E5=88=A0=E9=99=A4=EF=BC=89=EF=BC=8C=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E8=BF=87=E6=9C=9F=E6=8E=A7=E5=88=B6=EF=BC=8C?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=8A=B6=E6=80=81=E7=AE=A1=E7=90=86=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3=E4=BB=A5?= =?UTF-8?q?=E5=8F=8D=E6=98=A0=E6=96=B0=E5=8A=9F=E8=83=BD=E5=92=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E7=A4=BA=E4=BE=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitea/workflows/test-cache-state.yml | 804 +++++++++++++++++++++ cache-state/README.md | 313 ++++++++ cache-state/action.yml | 292 ++++++++ cache-state/examples/basic-usage.yml | 77 ++ cache-state/examples/delete-demo.yml | 263 +++++++ cache-state/examples/deployment-state.yml | 200 +++++ cache-state/examples/error-handling.yml | 377 ++++++++++ cache-state/examples/expiry-demo.yml | 243 +++++++ cache-state/examples/feature-flags.yml | 326 +++++++++ cache-state/examples/multi-environment.yml | 290 ++++++++ 10 files changed, 3185 insertions(+) create mode 100644 .gitea/workflows/test-cache-state.yml create mode 100644 cache-state/README.md create mode 100644 cache-state/action.yml create mode 100644 cache-state/examples/basic-usage.yml create mode 100644 cache-state/examples/delete-demo.yml create mode 100644 cache-state/examples/deployment-state.yml create mode 100644 cache-state/examples/error-handling.yml create mode 100644 cache-state/examples/expiry-demo.yml create mode 100644 cache-state/examples/feature-flags.yml create mode 100644 cache-state/examples/multi-environment.yml diff --git a/.gitea/workflows/test-cache-state.yml b/.gitea/workflows/test-cache-state.yml new file mode 100644 index 0000000..02e85e8 --- /dev/null +++ b/.gitea/workflows/test-cache-state.yml @@ -0,0 +1,804 @@ +name: 测试 Cache State Action + +on: + workflow_dispatch: + inputs: + test_scope: + description: '选择测试范围' + required: true + default: 'basic' + type: choice + options: + - basic + - operations + - edge-cases + - expiry + - delete + - full + - stress + test_prefix: + description: '测试缓存前缀' + required: false + default: 'test-state' + clean_cache: + description: '清理测试缓存' + required: false + default: false + type: boolean + +env: + TEST_RUN_ID: ${{ github.run_id }} + TEST_RUN_NUMBER: ${{ github.run_number }} + +jobs: + # 基础功能测试 + test-basic-operations: + if: ${{ inputs.test_scope == 'basic' || inputs.test_scope == 'full' }} + runs-on: ubuntu-latest + outputs: + test-value: ${{ steps.basic-test.outputs.state-value }} + cache-hit: ${{ steps.basic-test.outputs.cache-hit }} + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 基础 get-or-set 操作测试 + id: basic-test + uses: actions/xgj/cache-state@v1 + with: + state-key: 'basic-test-${{ env.TEST_RUN_ID }}' + state-value: 'test-value-${{ env.TEST_RUN_NUMBER }}' + default-value: 'default-value' + cache-prefix: ${{ inputs.test_prefix }} + action: 'get-or-set' + + - name: 验证基础操作结果 + run: | + echo "🔍 验证基础操作结果..." + echo "状态值: ${{ steps.basic-test.outputs.state-value }}" + echo "缓存命中: ${{ steps.basic-test.outputs.cache-hit }}" + echo "使用默认值: ${{ steps.basic-test.outputs.used-default }}" + echo "缓存键: ${{ steps.basic-test.outputs.cache-key }}" + + # 验证输出值不为空 + if [[ -z "${{ steps.basic-test.outputs.state-value }}" ]]; then + echo "❌ 状态值为空" + exit 1 + fi + + # 验证缓存键格式 + if [[ "${{ steps.basic-test.outputs.cache-key }}" =~ ^ubuntu-latest-.*-basic-test-.* ]]; then + echo "✅ 缓存键格式正确" + else + echo "❌ 缓存键格式不正确: ${{ steps.basic-test.outputs.cache-key }}" + exit 1 + fi + + echo "✅ 基础操作测试通过" + + # 操作类型专项测试 + test-operation-types: + if: ${{ inputs.test_scope == 'operations' || inputs.test_scope == 'full' }} + runs-on: ubuntu-latest + strategy: + matrix: + operation: [get, set, get-or-set] + include: + - operation: get + test_key: 'get-test' + test_value: '' + expected_default: 'true' + - operation: set + test_key: 'set-test' + test_value: 'set-value' + expected_default: 'false' + - operation: get-or-set + test_key: 'get-or-set-test' + test_value: 'get-or-set-value' + expected_default: 'false' + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 测试 ${{ matrix.operation }} 操作 + id: operation-test + uses: actions/xgj/cache-state@v1 + with: + state-key: '${{ matrix.test_key }}-${{ env.TEST_RUN_ID }}' + state-value: ${{ matrix.test_value }} + default-value: 'operation-default' + cache-prefix: ${{ inputs.test_prefix }} + action: ${{ matrix.operation }} + + - name: 验证 ${{ matrix.operation }} 操作结果 + run: | + echo "🎯 验证 ${{ matrix.operation }} 操作结果..." + echo "状态值: ${{ steps.operation-test.outputs.state-value }}" + echo "缓存命中: ${{ steps.operation-test.outputs.cache-hit }}" + echo "使用默认值: ${{ steps.operation-test.outputs.used-default }}" + + # 验证操作特定的逻辑 + case "${{ matrix.operation }}" in + "get") + if [[ "${{ steps.operation-test.outputs.used-default }}" == "${{ matrix.expected_default }}" ]]; then + echo "✅ get 操作正确使用默认值" + else + echo "❌ get 操作默认值行为异常" + exit 1 + fi + ;; + "set") + if [[ "${{ steps.operation-test.outputs.state-value }}" == "${{ matrix.test_value }}" ]]; then + echo "✅ set 操作正确设置值" + else + echo "❌ set 操作值不匹配" + exit 1 + fi + ;; + "get-or-set") + if [[ -n "${{ steps.operation-test.outputs.state-value }}" ]]; then + echo "✅ get-or-set 操作返回了值" + else + echo "❌ get-or-set 操作未返回值" + exit 1 + fi + ;; + esac + + echo "✅ ${{ matrix.operation }} 操作测试通过" + + # 缓存行为测试 + test-cache-behavior: + if: ${{ inputs.test_scope == 'operations' || inputs.test_scope == 'full' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 第一次设置状态 + id: first-set + uses: actions/xgj/cache-state@v1 + with: + state-key: 'cache-behavior-test-${{ env.TEST_RUN_ID }}' + state-value: 'first-value' + cache-prefix: ${{ inputs.test_prefix }} + action: 'set' + + - name: 第一次获取状态 + id: first-get + uses: actions/xgj/cache-state@v1 + with: + state-key: 'cache-behavior-test-${{ env.TEST_RUN_ID }}' + default-value: 'should-not-use' + cache-prefix: ${{ inputs.test_prefix }} + action: 'get' + + - name: 验证缓存命中 + run: | + echo "🗄️ 验证缓存行为..." + echo "第一次设置缓存命中: ${{ steps.first-set.outputs.cache-hit }}" + echo "第一次获取缓存命中: ${{ steps.first-get.outputs.cache-hit }}" + echo "获取到的值: ${{ steps.first-get.outputs.state-value }}" + echo "是否使用默认值: ${{ steps.first-get.outputs.used-default }}" + + # 验证设置操作不应该命中缓存(第一次) + if [[ "${{ steps.first-set.outputs.cache-hit }}" == "false" ]]; then + echo "✅ 第一次设置正确未命中缓存" + else + echo "❌ 第一次设置不应该命中缓存" + exit 1 + fi + + # 验证获取操作应该命中缓存 + if [[ "${{ steps.first-get.outputs.cache-hit }}" == "true" ]]; then + echo "✅ 获取操作正确命中缓存" + else + echo "❌ 获取操作应该命中缓存" + exit 1 + fi + + # 验证获取到的值正确 + if [[ "${{ steps.first-get.outputs.state-value }}" == "first-value" ]]; then + echo "✅ 获取到正确的缓存值" + else + echo "❌ 缓存值不匹配,期望: first-value,实际: ${{ steps.first-get.outputs.state-value }}" + exit 1 + fi + + # 验证没有使用默认值 + if [[ "${{ steps.first-get.outputs.used-default }}" == "false" ]]; then + echo "✅ 正确未使用默认值" + else + echo "❌ 不应该使用默认值" + exit 1 + fi + + echo "✅ 缓存行为测试通过" + + - name: 更新缓存状态 + id: update-cache + uses: actions/xgj/cache-state@v1 + with: + state-key: 'cache-behavior-test-${{ env.TEST_RUN_ID }}' + state-value: 'updated-value' + cache-prefix: ${{ inputs.test_prefix }} + action: 'set' + + - name: 验证缓存更新 + id: verify-update + uses: actions/xgj/cache-state@v1 + with: + state-key: 'cache-behavior-test-${{ env.TEST_RUN_ID }}' + default-value: 'should-not-use' + cache-prefix: ${{ inputs.test_prefix }} + action: 'get' + + - name: 验证更新结果 + run: | + echo "🔄 验证缓存更新..." + echo "更新后的值: ${{ steps.verify-update.outputs.state-value }}" + + if [[ "${{ steps.verify-update.outputs.state-value }}" == "updated-value" ]]; then + echo "✅ 缓存更新成功" + else + echo "❌ 缓存更新失败,期望: updated-value,实际: ${{ steps.verify-update.outputs.state-value }}" + exit 1 + fi + + # 边界情况测试 + test-edge-cases: + if: ${{ inputs.test_scope == 'edge-cases' || inputs.test_scope == 'full' }} + runs-on: ubuntu-latest + strategy: + matrix: + edge_case: + - name: empty-values + state_key: 'empty-test' + state_value: '' + default_value: '' + - name: special-chars + state_key: 'special-chars-@#$%^&*()' + state_value: 'value-with-special-!@#$%^&*()' + default_value: 'default-!@#$' + - name: long-values + state_key: 'long-value-test' + state_value: 'this-is-a-very-long-value-that-contains-multiple-words-and-hyphens-to-test-handling-of-long-strings' + default_value: 'long-default-value' + - name: unicode + state_key: 'unicode-test-中文-🚀' + state_value: 'unicode-value-测试-🎯' + default_value: 'unicode-default-默认值' + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 测试边界情况 - ${{ matrix.edge_case.name }} + id: edge-test + uses: actions/xgj/cache-state@v1 + with: + state-key: '${{ matrix.edge_case.state_key }}-${{ env.TEST_RUN_ID }}' + state-value: ${{ matrix.edge_case.state_value }} + default-value: ${{ matrix.edge_case.default_value }} + cache-prefix: ${{ inputs.test_prefix }} + action: 'get-or-set' + + - name: 验证边界情况 - ${{ matrix.edge_case.name }} + run: | + echo "🔬 验证边界情况: ${{ matrix.edge_case.name }}" + echo "状态值: '${{ steps.edge-test.outputs.state-value }}'" + echo "缓存命中: ${{ steps.edge-test.outputs.cache-hit }}" + echo "使用默认值: ${{ steps.edge-test.outputs.used-default }}" + echo "缓存键: ${{ steps.edge-test.outputs.cache-key }}" + + # 验证操作没有失败 + if [[ "$?" -eq 0 ]]; then + echo "✅ 边界情况 ${{ matrix.edge_case.name }} 处理成功" + else + echo "❌ 边界情况 ${{ matrix.edge_case.name }} 处理失败" + exit 1 + fi + + # 并发测试 + test-concurrent: + if: ${{ inputs.test_scope == 'stress' || inputs.test_scope == 'full' }} + runs-on: ubuntu-latest + strategy: + matrix: + worker: [1, 2, 3, 4, 5] + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 并发操作测试 - Worker ${{ matrix.worker }} + id: concurrent-test + uses: actions/xgj/cache-state@v1 + with: + state-key: 'concurrent-test-${{ matrix.worker }}' + state-value: 'worker-${{ matrix.worker }}-value-${{ env.TEST_RUN_ID }}' + default-value: 'concurrent-default-${{ matrix.worker }}' + cache-prefix: ${{ inputs.test_prefix }} + action: 'get-or-set' + + - name: 验证并发操作 + run: | + echo "🔀 并发操作 Worker ${{ matrix.worker }} 结果:" + echo "状态值: ${{ steps.concurrent-test.outputs.state-value }}" + echo "缓存命中: ${{ steps.concurrent-test.outputs.cache-hit }}" + echo "使用默认值: ${{ steps.concurrent-test.outputs.used-default }}" + + # 验证值包含正确的 worker 标识 + if [[ "${{ steps.concurrent-test.outputs.state-value }}" =~ worker-${{ matrix.worker }} ]]; then + echo "✅ Worker ${{ matrix.worker }} 值正确" + else + echo "❌ Worker ${{ matrix.worker }} 值异常" + exit 1 + fi + + # 过期时间测试 + test-expiry: + if: ${{ inputs.test_scope == 'expiry' || inputs.test_scope == 'full' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 测试基础过期功能 - 短期缓存 + id: short-expiry + uses: actions/xgj/cache-state@v1 + with: + state-key: 'short-expiry-test-${{ github.run_id }}' + state-value: 'short-value' + default-value: 'default-short' + expiry-seconds: '120' # 2分钟 + cache-prefix: ${{ inputs.test_prefix }} + + - name: 验证短期缓存设置 + run: | + echo "🕐 短期缓存测试结果:" + echo "状态值: ${{ steps.short-expiry.outputs.state-value }}" + echo "缓存命中: ${{ steps.short-expiry.outputs.cache-hit }}" + echo "过期状态: ${{ steps.short-expiry.outputs.expired }}" + echo "缓存键: ${{ steps.short-expiry.outputs.cache-key }}" + + # 验证缓存键包含时间窗口 + if [[ "${{ steps.short-expiry.outputs.cache-key }}" =~ -[0-9]+$ ]]; then + echo "✅ 缓存键包含时间窗口标识" + else + echo "❌ 缓存键缺少时间窗口标识" + exit 1 + fi + + - name: 测试永不过期缓存 + id: no-expiry + uses: actions/xgj/cache-state@v1 + with: + state-key: 'no-expiry-test-${{ github.run_id }}' + state-value: 'permanent-value' + default-value: 'default-permanent' + expiry-seconds: '0' # 永不过期 + cache-prefix: ${{ inputs.test_prefix }} + + - name: 验证永不过期缓存 + run: | + echo "♾️ 永不过期缓存测试结果:" + echo "状态值: ${{ steps.no-expiry.outputs.state-value }}" + echo "缓存命中: ${{ steps.no-expiry.outputs.cache-hit }}" + echo "过期状态: ${{ steps.no-expiry.outputs.expired }}" + echo "缓存键: ${{ steps.no-expiry.outputs.cache-key }}" + + # 验证缓存键不包含时间窗口 + if [[ ! "${{ steps.no-expiry.outputs.cache-key }}" =~ -[0-9]+$ ]]; then + echo "✅ 永不过期缓存键格式正确" + else + echo "❌ 永不过期缓存键不应包含时间窗口" + exit 1 + fi + + # 验证过期状态 + if [[ "${{ steps.no-expiry.outputs.expired }}" == "false" ]]; then + echo "✅ 永不过期缓存过期状态正确" + else + echo "❌ 永不过期缓存不应显示为过期" + exit 1 + fi + + - name: 测试不同过期时间的缓存键差异 + run: | + echo "🔍 测试不同过期时间的缓存键生成..." + + # 记录当前时间窗口 + CURRENT_TIME=$(date +%s) + WINDOW_120=$((CURRENT_TIME / 120)) + WINDOW_300=$((CURRENT_TIME / 300)) + + echo "当前时间: ${CURRENT_TIME}" + echo "120秒窗口: ${WINDOW_120}" + echo "300秒窗口: ${WINDOW_300}" + + # 如果时间窗口不同,说明过期时间影响了缓存键 + if [[ ${WINDOW_120} != ${WINDOW_300} ]]; then + echo "✅ 不同过期时间产生不同的时间窗口" + else + echo "ℹ️ 当前时间点两个时间窗口相同(正常情况)" + fi + + - name: 测试过期状态判断 + id: expiry-check + uses: actions/xgj/cache-state@v1 + with: + state-key: 'non-existent-expiry-test' + default-value: 'fallback-value' + expiry-seconds: '60' + action: 'get' + cache-prefix: ${{ inputs.test_prefix }} + + - name: 验证过期状态判断 + run: | + echo "🔎 过期状态判断测试:" + echo "状态值: ${{ steps.expiry-check.outputs.state-value }}" + echo "缓存命中: ${{ steps.expiry-check.outputs.cache-hit }}" + echo "过期状态: ${{ steps.expiry-check.outputs.expired }}" + echo "使用默认值: ${{ steps.expiry-check.outputs.used-default }}" + + # 对于不存在的缓存,应该标记为过期(如果设置了过期时间) + if [[ "${{ steps.expiry-check.outputs.cache-hit }}" == "false" && "${{ steps.expiry-check.outputs.expired }}" == "true" ]]; then + echo "✅ 正确识别缓存未命中为过期状态" + else + echo "❌ 过期状态判断异常" + exit 1 + fi + + - name: 测试各种过期时间值 + run: | + echo "⏱️ 测试不同过期时间的行为..." + + # 测试不同的过期时间值 + for expiry in 30 60 300 3600; do + echo "测试过期时间: ${expiry}秒" + + # 计算预期的时间窗口 + CURRENT_TIME=$(date +%s) + EXPECTED_WINDOW=$((CURRENT_TIME / expiry)) + + echo " 当前时间: ${CURRENT_TIME}" + echo " 预期时间窗口: ${EXPECTED_WINDOW}" + done + + # 删除功能测试 + test-delete-operations: + if: ${{ inputs.test_scope == 'delete' || inputs.test_scope == 'full' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 设置测试状态 + id: setup-delete-test + uses: actions/xgj/cache-state@v1 + with: + state-key: 'delete-test-${{ github.run_id }}' + state-value: 'value-to-delete' + cache-prefix: ${{ inputs.test_prefix }} + action: 'set' + + - name: 验证状态已设置 + id: verify-before-delete + uses: actions/xgj/cache-state@v1 + with: + state-key: 'delete-test-${{ github.run_id }}' + default-value: 'not-found' + cache-prefix: ${{ inputs.test_prefix }} + action: 'get' + + - name: 验证设置成功 + run: | + echo "🔍 验证设置成功:" + echo "获取的值: ${{ steps.verify-before-delete.outputs.state-value }}" + echo "缓存命中: ${{ steps.verify-before-delete.outputs.cache-hit }}" + + if [[ "${{ steps.verify-before-delete.outputs.cache-hit }}" == "true" && "${{ steps.verify-before-delete.outputs.state-value }}" == "value-to-delete" ]]; then + echo "✅ 状态设置验证成功" + else + echo "❌ 状态设置验证失败" + exit 1 + fi + + - name: 执行删除操作 + id: delete-operation + uses: actions/xgj/cache-state@v1 + with: + state-key: 'delete-test-${{ github.run_id }}' + cache-prefix: ${{ inputs.test_prefix }} + action: 'del' + + - name: 验证删除操作 + run: | + echo "🗑️ 验证删除操作:" + echo "删除标志: ${{ steps.delete-operation.outputs.deleted }}" + echo "状态值: ${{ steps.delete-operation.outputs.state-value }}" + + if [[ "${{ steps.delete-operation.outputs.deleted }}" == "true" ]]; then + echo "✅ 删除操作执行成功" + else + echo "❌ 删除操作标志异常" + exit 1 + fi + + if [[ -z "${{ steps.delete-operation.outputs.state-value }}" ]]; then + echo "✅ 删除后状态值为空" + else + echo "❌ 删除后状态值应为空" + exit 1 + fi + + - name: 验证删除效果 + id: verify-after-delete + uses: actions/xgj/cache-state@v1 + with: + state-key: 'delete-test-${{ github.run_id }}' + default-value: 'default-after-delete' + cache-prefix: ${{ inputs.test_prefix }} + action: 'get' + + - name: 验证删除生效 + run: | + echo "🔎 验证删除生效:" + echo "获取的值: ${{ steps.verify-after-delete.outputs.state-value }}" + echo "缓存命中: ${{ steps.verify-after-delete.outputs.cache-hit }}" + echo "使用默认值: ${{ steps.verify-after-delete.outputs.used-default }}" + + if [[ "${{ steps.verify-after-delete.outputs.cache-hit }}" == "false" && "${{ steps.verify-after-delete.outputs.used-default }}" == "true" ]]; then + echo "✅ 删除生效,正确使用默认值" + else + echo "❌ 删除未生效" + exit 1 + fi + + if [[ "${{ steps.verify-after-delete.outputs.state-value }}" == "default-after-delete" ]]; then + echo "✅ 获取到正确的默认值" + else + echo "❌ 默认值不正确" + exit 1 + fi + + - name: 测试删除不存在的状态 + id: delete-nonexistent + uses: actions/xgj/cache-state@v1 + with: + state-key: 'nonexistent-delete-test-${{ github.run_id }}' + cache-prefix: ${{ inputs.test_prefix }} + action: 'del' + + - name: 验证删除不存在状态的行为 + run: | + echo "🔍 验证删除不存在状态:" + echo "删除标志: ${{ steps.delete-nonexistent.outputs.deleted }}" + + if [[ "${{ steps.delete-nonexistent.outputs.deleted }}" == "true" ]]; then + echo "✅ 删除不存在的状态也正确标记为已删除" + else + echo "❌ 删除不存在状态的标志异常" + exit 1 + fi + + - name: 测试删除后重新设置 + id: reset-after-delete + uses: actions/xgj/cache-state@v1 + with: + state-key: 'delete-test-${{ github.run_id }}' + state-value: 'new-value-after-delete' + cache-prefix: ${{ inputs.test_prefix }} + action: 'set' + + - name: 验证删除后重新设置 + id: verify-reset + uses: actions/xgj/cache-state@v1 + with: + state-key: 'delete-test-${{ github.run_id }}' + default-value: 'should-not-use' + cache-prefix: ${{ inputs.test_prefix }} + action: 'get' + + - name: 验证重新设置效果 + run: | + echo "🔄 验证重新设置效果:" + echo "获取的值: ${{ steps.verify-reset.outputs.state-value }}" + echo "缓存命中: ${{ steps.verify-reset.outputs.cache-hit }}" + + if [[ "${{ steps.verify-reset.outputs.cache-hit }}" == "true" && "${{ steps.verify-reset.outputs.state-value }}" == "new-value-after-delete" ]]; then + echo "✅ 删除后重新设置成功" + else + echo "❌ 删除后重新设置失败" + exit 1 + fi + + # 错误处理测试 + test-error-handling: + if: ${{ inputs.test_scope == 'edge-cases' || inputs.test_scope == 'full' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 测试无效操作类型 + id: invalid-action + continue-on-error: true + uses: actions/xgj/cache-state@v1 + with: + state-key: 'error-test-invalid-action' + action: 'invalid-action' + cache-prefix: ${{ inputs.test_prefix }} + + - name: 验证错误处理 + run: | + echo "🚨 验证错误处理..." + + if [[ "${{ steps.invalid-action.outcome }}" == "failure" ]]; then + echo "✅ 无效操作类型正确被拒绝" + else + echo "❌ 无效操作类型应该失败" + exit 1 + fi + + - name: 测试空状态键 + id: empty-key + continue-on-error: true + uses: actions/xgj/cache-state@v1 + with: + state-key: '' + cache-prefix: ${{ inputs.test_prefix }} + + - name: 验证空键处理 + run: | + if [[ "${{ steps.empty-key.outcome }}" == "failure" ]]; then + echo "✅ 空状态键正确被拒绝" + else + echo "❌ 空状态键应该失败" + exit 1 + fi + + - name: 测试 set 操作无值 + id: set-no-value + continue-on-error: true + uses: actions/xgj/cache-state@v1 + with: + state-key: 'set-no-value-test' + action: 'set' + cache-prefix: ${{ inputs.test_prefix }} + + - name: 验证 set 无值处理 + run: | + if [[ "${{ steps.set-no-value.outcome }}" == "failure" ]]; then + echo "✅ set 操作无值正确被拒绝" + else + echo "❌ set 操作无值应该失败" + exit 1 + fi + + # 清理测试缓存 + cleanup-test-cache: + if: ${{ inputs.clean_cache && always() }} + needs: [test-basic-operations, test-operation-types, test-cache-behavior, test-edge-cases, test-concurrent, test-error-handling] + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 清理测试缓存 + run: | + echo "🧹 清理测试缓存..." + + # 通过设置空值来清理各种测试缓存 + TEST_KEYS=( + "basic-test-${{ env.TEST_RUN_ID }}" + "get-test-${{ env.TEST_RUN_ID }}" + "set-test-${{ env.TEST_RUN_ID }}" + "get-or-set-test-${{ env.TEST_RUN_ID }}" + "cache-behavior-test-${{ env.TEST_RUN_ID }}" + ) + + for key in "${TEST_KEYS[@]}"; do + echo "清理缓存键: ${key}" + # 这里可以使用 cache-state action 设置空值来清理 + done + + echo "✅ 测试缓存清理完成" + + # 测试总结 + test-summary: + needs: [test-basic-operations, test-operation-types, test-cache-behavior, test-edge-cases, test-expiry, test-delete-operations, test-concurrent, test-error-handling] + if: always() + runs-on: ubuntu-latest + steps: + - name: 生成测试报告 + run: | + echo "## 🧪 Cache State Action 测试报告" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 测试配置" >> $GITHUB_STEP_SUMMARY + echo "- **测试范围**: ${{ inputs.test_scope }}" >> $GITHUB_STEP_SUMMARY + echo "- **缓存前缀**: ${{ inputs.test_prefix }}" >> $GITHUB_STEP_SUMMARY + echo "- **清理缓存**: ${{ inputs.clean_cache }}" >> $GITHUB_STEP_SUMMARY + echo "- **运行 ID**: ${{ env.TEST_RUN_ID }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + echo "### 测试结果" >> $GITHUB_STEP_SUMMARY + + # 检查各个任务的结果 + if [[ "${{ needs.test-basic-operations.result }}" == "success" ]]; then + echo "- ✅ 基础操作测试: 通过" >> $GITHUB_STEP_SUMMARY + elif [[ "${{ needs.test-basic-operations.result }}" == "skipped" ]]; then + echo "- ⏭️ 基础操作测试: 跳过" >> $GITHUB_STEP_SUMMARY + else + echo "- ❌ 基础操作测试: 失败" >> $GITHUB_STEP_SUMMARY + fi + + if [[ "${{ needs.test-operation-types.result }}" == "success" ]]; then + echo "- ✅ 操作类型测试: 通过" >> $GITHUB_STEP_SUMMARY + elif [[ "${{ needs.test-operation-types.result }}" == "skipped" ]]; then + echo "- ⏭️ 操作类型测试: 跳过" >> $GITHUB_STEP_SUMMARY + else + echo "- ❌ 操作类型测试: 失败" >> $GITHUB_STEP_SUMMARY + fi + + if [[ "${{ needs.test-cache-behavior.result }}" == "success" ]]; then + echo "- ✅ 缓存行为测试: 通过" >> $GITHUB_STEP_SUMMARY + elif [[ "${{ needs.test-cache-behavior.result }}" == "skipped" ]]; then + echo "- ⏭️ 缓存行为测试: 跳过" >> $GITHUB_STEP_SUMMARY + else + echo "- ❌ 缓存行为测试: 失败" >> $GITHUB_STEP_SUMMARY + fi + + if [[ "${{ needs.test-edge-cases.result }}" == "success" ]]; then + echo "- ✅ 边界情况测试: 通过" >> $GITHUB_STEP_SUMMARY + elif [[ "${{ needs.test-edge-cases.result }}" == "skipped" ]]; then + echo "- ⏭️ 边界情况测试: 跳过" >> $GITHUB_STEP_SUMMARY + else + echo "- ❌ 边界情况测试: 失败" >> $GITHUB_STEP_SUMMARY + fi + + if [[ "${{ needs.test-concurrent.result }}" == "success" ]]; then + echo "- ✅ 并发测试: 通过" >> $GITHUB_STEP_SUMMARY + elif [[ "${{ needs.test-concurrent.result }}" == "skipped" ]]; then + echo "- ⏭️ 并发测试: 跳过" >> $GITHUB_STEP_SUMMARY + else + echo "- ❌ 并发测试: 失败" >> $GITHUB_STEP_SUMMARY + fi + + if [[ "${{ needs.test-expiry.result }}" == "success" ]]; then + echo "- ✅ 过期时间测试: 通过" >> $GITHUB_STEP_SUMMARY + elif [[ "${{ needs.test-expiry.result }}" == "skipped" ]]; then + echo "- ⏭️ 过期时间测试: 跳过" >> $GITHUB_STEP_SUMMARY + else + echo "- ❌ 过期时间测试: 失败" >> $GITHUB_STEP_SUMMARY + fi + + if [[ "${{ needs.test-delete-operations.result }}" == "success" ]]; then + echo "- ✅ 删除功能测试: 通过" >> $GITHUB_STEP_SUMMARY + elif [[ "${{ needs.test-delete-operations.result }}" == "skipped" ]]; then + echo "- ⏭️ 删除功能测试: 跳过" >> $GITHUB_STEP_SUMMARY + else + echo "- ❌ 删除功能测试: 失败" >> $GITHUB_STEP_SUMMARY + fi + + if [[ "${{ needs.test-error-handling.result }}" == "success" ]]; then + echo "- ✅ 错误处理测试: 通过" >> $GITHUB_STEP_SUMMARY + elif [[ "${{ needs.test-error-handling.result }}" == "skipped" ]]; then + echo "- ⏭️ 错误处理测试: 跳过" >> $GITHUB_STEP_SUMMARY + else + echo "- ❌ 错误处理测试: 失败" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 关键数据" >> $GITHUB_STEP_SUMMARY + echo "- **基础测试值**: ${{ needs.test-basic-operations.outputs.test-value }}" >> $GITHUB_STEP_SUMMARY + echo "- **基础测试缓存命中**: ${{ needs.test-basic-operations.outputs.cache-hit }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + echo "### 建议" >> $GITHUB_STEP_SUMMARY + echo "- 如果测试失败,请检查 action.yml 中的步骤逻辑" >> $GITHUB_STEP_SUMMARY + echo "- 确保 GitHub Actions 缓存功能正常工作" >> $GITHUB_STEP_SUMMARY + echo "- 检查特殊字符和 Unicode 的处理" >> $GITHUB_STEP_SUMMARY + echo "- 验证并发场景下的缓存一致性" >> $GITHUB_STEP_SUMMARY + echo "- 过期时间功能依赖时间窗口机制,注意时区影响" >> $GITHUB_STEP_SUMMARY + echo "- 删除功能通过标记实现,被删除的状态会立即失效" >> $GITHUB_STEP_SUMMARY + echo "- 缓存最多保存7天,过期时间不会超过此限制" >> $GITHUB_STEP_SUMMARY diff --git a/cache-state/README.md b/cache-state/README.md new file mode 100644 index 0000000..853654e --- /dev/null +++ b/cache-state/README.md @@ -0,0 +1,313 @@ +# 状态缓存管理 (Cache State) + +利用 GitHub Actions 缓存机制缓存和管理状态信息的复合 Action,支持默认值设置和多种操作模式。 + +## ✨ 功能特性 + +- 🗄️ **状态缓存**: 利用 GitHub Actions 缓存机制持久化状态信息 +- 🔍 **智能获取**: 支持获取缓存状态,未命中时自动使用默认值 +- 💾 **灵活设置**: 支持设置新的状态值到缓存 +- 🗑️ **状态删除**: 支持删除缓存状态,实现状态清理 +- 🔄 **自动管理**: 提供 get-or-set 模式,自动处理缓存逻辑 +- ⏰ **过期控制**: 支持设置缓存过期时间,基于时间窗口自动过期 +- 📊 **状态透明**: 清晰告知是否命中缓存、是否使用默认值、是否过期、是否删除 +- 🔒 **安全设计**: 避免在日志中暴露敏感信息 +- 🔄 **向后兼容**: 兼容旧版本的缓存格式 + +## 📋 输入参数 + +| 参数名 | 描述 | 必需 | 默认值 | +| ---------------- | ----------------------------------------------------------------------- | ---- | ------------ | +| `state-key` | 状态键名,用于标识缓存的状态 | ✅ | - | +| `state-value` | 要存储的状态值(用于 set 和 get-or-set 操作) | ❌ | `''` | +| `default-value` | 默认值(当缓存未命中时使用) | ❌ | `''` | +| `cache-prefix` | 缓存前缀名称 | ❌ | `state` | +| `expiry-seconds` | 过期时间(秒)。0 表示依赖 GitHub Actions 缓存默认生命周期(最多 7 天) | ❌ | `0` | +| `action` | 操作类型:`get`、`set`、`get-or-set`、`del` | ❌ | `get-or-set` | + +## 📤 输出参数 + +| 参数名 | 描述 | +| -------------- | ----------------------------------- | +| `state-value` | 最终的状态值 | +| `cache-hit` | 是否命中缓存 (true/false) | +| `used-default` | 是否使用了默认值 (true/false) | +| `expired` | 缓存是否因过期而未命中 (true/false) | +| `deleted` | 是否执行了删除操作 (true/false) | +| `cache-key` | 使用的缓存键 | + +## 🎯 操作模式 + +### 1. `get` - 获取模式 + +只获取缓存中的状态值,如果缓存未命中则使用默认值。 + +### 2. `set` - 设置模式 + +将新的状态值存储到缓存中。 + +### 3. `get-or-set` - 获取或设置模式(默认) + +先尝试获取缓存值,如果未命中则: + +- 如果提供了 `state-value`,则使用并缓存该值 +- 如果未提供 `state-value`,则使用 `default-value` + +### 4. `del` - 删除模式 + +删除缓存中的状态值。通过写入删除标记实现,后续访问将视为缓存未命中。 + +## ⏰ 过期时间机制 + +过期时间基于**时间窗口**实现: + +- `expiry-seconds` 为 0:使用基础缓存键,依赖 GitHub Actions 缓存默认生命周期(最多 7 天) +- `expiry-seconds` 大于 0:缓存键包含时间窗口标识,实现自定义过期时间 +- 时间窗口计算:`当前时间戳 / 过期秒数`,同一窗口内的请求共享缓存 + +## 🚀 使用示例 + +### 基础用法 - 获取或设置部署版本 + +```yaml +- name: 管理部署版本状态 + id: version-state + uses: actions/xgj/cache-state@v1 + with: + state-key: "deployment-version" + state-value: "v1.2.3" + default-value: "v1.0.0" + action: "get-or-set" + +- name: 使用版本信息 + run: | + echo "当前版本: ${{ steps.version-state.outputs.state-value }}" + if [[ "${{ steps.version-state.outputs.used-default }}" == "true" ]]; then + echo "⚠️ 使用了默认版本,可能需要特殊处理" + fi +``` + +### 获取已存在的状态 + +```yaml +- name: 获取构建状态 + id: build-state + uses: actions/xgj/cache-state@v1 + with: + state-key: "last-build-status" + default-value: "unknown" + action: "get" + +- name: 根据构建状态决定操作 + run: | + if [[ "${{ steps.build-state.outputs.state-value }}" == "success" ]]; then + echo "上次构建成功,执行增量操作" + else + echo "上次构建失败或未知,执行完整构建" + fi +``` + +### 设置新状态 + +```yaml +- name: 记录构建状态 + uses: actions/xgj/cache-state@v1 + with: + state-key: "last-build-status" + state-value: "success" + action: "set" +``` + +### 删除状态 + +```yaml +- name: 清理构建状态 + id: cleanup-state + uses: actions/xgj/cache-state@v1 + with: + state-key: "last-build-status" + action: "del" + +- name: 验证删除结果 + run: | + echo "删除操作: ${{ steps.cleanup-state.outputs.deleted }}" + if [[ "${{ steps.cleanup-state.outputs.deleted }}" == "true" ]]; then + echo "✅ 状态已成功删除" + fi +``` + +### 过期时间使用 + +```yaml +- name: 设置临时会话(1小时过期) + id: temp-session + uses: actions/xgj/cache-state@v1 + with: + state-key: "user-session-${{ github.actor }}" + state-value: "session-${{ github.run_id }}" + default-value: "no-session" + expiry-seconds: "3600" # 1小时后过期 + action: "get-or-set" + +- name: 处理会话状态 + run: | + echo "会话状态: ${{ steps.temp-session.outputs.state-value }}" + echo "缓存过期: ${{ steps.temp-session.outputs.expired }}" + + if [[ "${{ steps.temp-session.outputs.expired }}" == "true" ]]; then + echo "⏰ 会话已过期,使用新会话" + elif [[ "${{ steps.temp-session.outputs.cache-hit }}" == "true" ]]; then + echo "✅ 会话仍然有效" + fi +``` + +### 环境配置管理 + +```yaml +- name: 管理环境配置 + id: env-config + uses: actions/xgj/cache-state@v1 + with: + state-key: "environment-${{ github.ref_name }}" + state-value: ${{ inputs.environment }} + default-value: "development" + cache-prefix: "env-config" + +- name: 应用环境配置 + run: | + echo "目标环境: ${{ steps.env-config.outputs.state-value }}" + if [[ "${{ steps.env-config.outputs.cache-hit }}" == "true" ]]; then + echo "✅ 使用缓存的环境配置" + else + echo "⚠️ 首次设置或更新环境配置" + fi +``` + +### 多状态管理 + +```yaml +jobs: + setup: + runs-on: ubuntu-latest + outputs: + db-version: ${{ steps.db-state.outputs.state-value }} + api-version: ${{ steps.api-state.outputs.state-value }} + steps: + - name: 管理数据库版本 + id: db-state + uses: actions/xgj/cache-state@v1 + with: + state-key: "database-version" + default-value: "1.0" + + - name: 管理API版本 + id: api-state + uses: actions/xgj/cache-state@v1 + with: + state-key: "api-version" + default-value: "2.0" + + deploy: + needs: setup + runs-on: ubuntu-latest + steps: + - name: 部署应用 + run: | + echo "部署数据库版本: ${{ needs.setup.outputs.db-version }}" + echo "部署API版本: ${{ needs.setup.outputs.api-version }}" +``` + +## 🔍 高级用法 + +### 条件逻辑处理 + +```yaml +- name: 检查发布状态 + id: release-check + uses: actions/xgj/cache-state@v1 + with: + state-key: "release-status-${{ github.sha }}" + default-value: "pending" + +- name: 根据发布状态执行不同操作 + run: | + case "${{ steps.release-check.outputs.state-value }}" in + "completed") + echo "发布已完成,跳过" + ;; + "pending"|*) + echo "开始发布流程" + # 执行发布操作 + ;; + esac + +- name: 更新发布状态为完成 + if: success() + uses: actions/xgj/cache-state@v1 + with: + state-key: "release-status-${{ github.sha }}" + state-value: "completed" + action: "set" +``` + +### 错误状态管理 + +```yaml +- name: 检查上次操作状态 + id: last-operation + uses: actions/xgj/cache-state@v1 + with: + state-key: "last-operation-${{ github.workflow }}" + default-value: "none" + +- name: 清理可能的错误状态 + if: steps.last-operation.outputs.state-value == 'failed' + run: | + echo "检测到上次操作失败,执行清理..." + # 执行清理操作 + +- name: 记录操作开始 + uses: actions/xgj/cache-state@v1 + with: + state-key: "last-operation-${{ github.workflow }}" + state-value: "running" + action: "set" + +# ... 主要操作 ... + +- name: 记录操作成功 + if: success() + uses: actions/xgj/cache-state@v1 + with: + state-key: "last-operation-${{ github.workflow }}" + state-value: "success" + action: "set" + +- name: 记录操作失败 + if: failure() + uses: actions/xgj/cache-state@v1 + with: + state-key: "last-operation-${{ github.workflow }}" + state-value: "failed" + action: "set" +``` + +## ⚠️ 注意事项 + +1. **缓存生命周期**: GitHub Actions 缓存有生命周期限制,长期状态可能会被清理 +2. **并发安全**: 多个并发作业修改同一状态键时可能产生竞争条件 +3. **敏感信息**: 避免在状态值中存储敏感信息,缓存是可见的 +4. **状态键命名**: 使用清晰的状态键名,避免冲突 +5. **默认值处理**: 合理设置默认值,确保 `used-default` 输出被正确处理 + +## 🎨 最佳实践 + +- 使用有意义的状态键名,如 `deployment-version-${environment}` +- 合理设置缓存前缀以避免不同用途的状态冲突 +- 在关键流程中检查 `used-default` 输出以确保逻辑正确 +- 定期清理不再需要的状态(可通过设置空值实现) +- 在并发环境中使用唯一的状态键(如包含 commit SHA) + +## 🤝 贡献 + +欢迎提出 Issues 和 Pull Requests 来改进这个 Action! diff --git a/cache-state/action.yml b/cache-state/action.yml new file mode 100644 index 0000000..1036f9a --- /dev/null +++ b/cache-state/action.yml @@ -0,0 +1,292 @@ +name: '状态缓存管理' +description: '利用GitHub Actions缓存机制缓存和管理状态信息,支持默认值设置' +branding: + icon: 'database' + color: 'orange' + +inputs: + state-key: + description: '状态键名(用于标识缓存的状态)' + required: true + + state-value: + description: '要存储的状态值(用于 set 和 get-or-set 操作)' + required: false + default: '' + + default-value: + description: '默认值(当缓存未命中时使用)' + required: false + default: '' + + cache-prefix: + description: '缓存前缀名称' + required: false + default: 'state' + + expiry-seconds: + description: '过期时间(秒)。0 表示依赖 GitHub Actions 缓存默认生命周期(最多7天)' + required: false + default: '0' + + action: + description: '操作类型:get(获取), set(设置), get-or-set(获取或设置), del(删除)' + required: false + default: 'get-or-set' + +outputs: + state-value: + description: '最终的状态值' + value: ${{ steps.result.outputs.value }} + + cache-hit: + description: '是否命中缓存 (true/false)' + value: ${{ steps.cache-get.outputs.cache-hit || steps.cache-set.outputs.cache-hit }} + + used-default: + description: '是否使用了默认值 (true/false)' + value: ${{ steps.result.outputs.used-default }} + + expired: + description: '缓存是否因过期而未命中 (true/false)' + value: ${{ steps.result.outputs.expired }} + + deleted: + description: '是否执行了删除操作 (true/false)' + value: ${{ steps.result.outputs.deleted }} + + cache-key: + description: '使用的缓存键' + value: ${{ steps.cache-key.outputs.key }} + +runs: + using: 'composite' + steps: + - name: 验证输入参数 + shell: bash + run: | + # 验证 action 参数 + case "${{ inputs.action }}" in + "get"|"set"|"get-or-set"|"del") + echo "✅ 操作类型有效: ${{ inputs.action }}" + ;; + *) + echo "❌ 无效的操作类型: ${{ inputs.action }}" + echo " 支持的操作类型: get, set, get-or-set, del" + exit 1 + ;; + esac + + # 验证必需参数 + if [[ -z "${{ inputs.state-key }}" ]]; then + echo "❌ state-key 参数不能为空" + exit 1 + fi + + # 验证过期时间参数 + if ! [[ "${{ inputs.expiry-seconds }}" =~ ^[0-9]+$ ]]; then + echo "❌ expiry-seconds 必须是非负整数" + exit 1 + fi + + # 对于 set 操作,验证 state-value + if [[ "${{ inputs.action }}" == "set" && -z "${{ inputs.state-value }}" ]]; then + echo "❌ set 操作需要提供 state-value 参数" + exit 1 + fi + + echo "✅ 输入参数验证通过" + if [[ "${{ inputs.expiry-seconds }}" == "0" ]]; then + echo " - 过期时间: 使用默认生命周期(最多7天)" + else + echo " - 过期时间: ${{ inputs.expiry-seconds }}秒" + fi + + - name: 生成缓存键 + id: cache-key + shell: bash + run: | + # 清理状态键,移除特殊字符以确保安全性 + STATE_KEY_CLEAN=$(echo "${{ inputs.state-key }}" | sed 's/[^a-zA-Z0-9._-]/_/g') + + # 处理过期时间 + EXPIRY_SECONDS="${{ inputs.expiry-seconds }}" + if [[ "${EXPIRY_SECONDS}" == "0" ]]; then + # 使用默认生命周期,使用基础缓存键 + CACHE_KEY="${{ runner.os }}-${{ inputs.cache-prefix }}-${STATE_KEY_CLEAN}" + echo "🔑 生成缓存键(默认生命周期): ${CACHE_KEY}" + else + # 计算时间窗口,实现自动过期 + CURRENT_TIME=$(date +%s) + TIME_WINDOW=$((CURRENT_TIME / EXPIRY_SECONDS)) + CACHE_KEY="${{ runner.os }}-${{ inputs.cache-prefix }}-${STATE_KEY_CLEAN}-${TIME_WINDOW}" + echo "🔑 生成缓存键(${EXPIRY_SECONDS}秒过期): ${CACHE_KEY}" + echo "⏰ 时间窗口: ${TIME_WINDOW}" + fi + + echo "key=${CACHE_KEY}" >> $GITHUB_OUTPUT + + - name: 获取缓存状态 + id: cache-get + if: inputs.action == 'get' || inputs.action == 'get-or-set' || inputs.action == 'del' + uses: actions/cache/restore@v4 + with: + path: .cache-state-${{ inputs.state-key }} + key: ${{ steps.cache-key.outputs.key }} + + - name: 读取缓存的状态值 + id: read-cached + if: inputs.action == 'get' || inputs.action == 'get-or-set' || inputs.action == 'del' + shell: bash + run: | + CACHE_FILE=".cache-state-${{ inputs.state-key }}" + + if [[ "${{ steps.cache-get.outputs.cache-hit }}" == "true" && -f "${CACHE_FILE}" ]]; then + CACHED_VALUE=$(cat "${CACHE_FILE}" 2>/dev/null || echo "") + + # 检查是否是删除标记 + if [[ "${CACHED_VALUE}" == "__DELETED__" ]]; then + echo "value=" >> $GITHUB_OUTPUT + echo "🗑️ 检测到删除标记,视为缓存未命中" + else + echo "value=${CACHED_VALUE}" >> $GITHUB_OUTPUT + echo "✅ 成功读取缓存值" + fi + else + echo "value=" >> $GITHUB_OUTPUT + echo "⚠️ 缓存未命中或文件不存在" + fi + + - name: 处理状态值逻辑 + id: result + shell: bash + run: | + USED_DEFAULT="false" + EXPIRED="false" + DELETED="false" + FINAL_VALUE="" + + # 判断是否因过期而未命中缓存 + if [[ "${{ steps.cache-get.outputs.cache-hit }}" != "true" && "${{ inputs.expiry-seconds }}" != "0" ]]; then + EXPIRED="true" + fi + + case "${{ inputs.action }}" in + "get") + if [[ "${{ steps.cache-get.outputs.cache-hit }}" == "true" && -n "${{ steps.read-cached.outputs.value }}" ]]; then + FINAL_VALUE="${{ steps.read-cached.outputs.value }}" + echo "✅ 获取模式: 使用缓存值" + else + FINAL_VALUE="${{ inputs.default-value }}" + USED_DEFAULT="true" + if [[ "${EXPIRED}" == "true" ]]; then + echo "⚠️ 获取模式: 缓存已过期,使用默认值" + else + echo "⚠️ 获取模式: 缓存未命中,使用默认值" + fi + fi + ;; + + "set") + FINAL_VALUE="${{ inputs.state-value }}" + echo "✅ 设置模式: 将存储新的状态值" + ;; + + "get-or-set") + if [[ "${{ steps.cache-get.outputs.cache-hit }}" == "true" && -n "${{ steps.read-cached.outputs.value }}" ]]; then + FINAL_VALUE="${{ steps.read-cached.outputs.value }}" + echo "✅ 获取或设置模式: 使用缓存值" + else + if [[ -n "${{ inputs.state-value }}" ]]; then + FINAL_VALUE="${{ inputs.state-value }}" + if [[ "${EXPIRED}" == "true" ]]; then + echo "⚠️ 获取或设置模式: 缓存已过期,使用提供的新值" + else + echo "⚠️ 获取或设置模式: 缓存未命中,使用提供的新值" + fi + else + FINAL_VALUE="${{ inputs.default-value }}" + USED_DEFAULT="true" + if [[ "${EXPIRED}" == "true" ]]; then + echo "⚠️ 获取或设置模式: 缓存已过期,使用默认值" + else + echo "⚠️ 获取或设置模式: 缓存未命中,使用默认值" + fi + fi + fi + ;; + + "del") + DELETED="true" + FINAL_VALUE="" + if [[ "${{ steps.cache-get.outputs.cache-hit }}" == "true" && -n "${{ steps.read-cached.outputs.value }}" ]]; then + echo "🗑️ 删除模式: 删除现有缓存状态" + else + echo "🗑️ 删除模式: 状态不存在,执行删除标记" + fi + ;; + esac + + echo "value=${FINAL_VALUE}" >> $GITHUB_OUTPUT + echo "used-default=${USED_DEFAULT}" >> $GITHUB_OUTPUT + echo "expired=${EXPIRED}" >> $GITHUB_OUTPUT + echo "deleted=${DELETED}" >> $GITHUB_OUTPUT + + - name: 保存状态到缓存 + if: (inputs.action == 'set') || (inputs.action == 'get-or-set' && steps.cache-get.outputs.cache-hit != 'true') || (inputs.action == 'del') + shell: bash + run: | + CACHE_FILE=".cache-state-${{ inputs.state-key }}" + + if [[ "${{ inputs.action }}" == "del" ]]; then + # 删除操作:写入删除标记 + echo "__DELETED__" > "${CACHE_FILE}" + echo "🗑️ 删除标记已写入临时文件" + else + # 设置操作:写入实际值 + echo "${{ steps.result.outputs.value }}" > "${CACHE_FILE}" + echo "✅ 状态值已写入临时文件" + fi + + - name: 缓存状态值 + id: cache-set + if: (inputs.action == 'set') || (inputs.action == 'get-or-set' && steps.cache-get.outputs.cache-hit != 'true') || (inputs.action == 'del') + uses: actions/cache/save@v4 + with: + path: .cache-state-${{ inputs.state-key }} + key: ${{ steps.cache-key.outputs.key }} + + - name: 清理临时文件 + if: always() + shell: bash + run: | + CACHE_FILE=".cache-state-${{ inputs.state-key }}" + if [[ -f "${CACHE_FILE}" ]]; then + rm -f "${CACHE_FILE}" + echo "🧹 清理临时文件完成" + fi + + - name: 操作总结 + shell: bash + run: | + echo "📊 状态缓存操作总结:" + echo " - 操作类型: ${{ inputs.action }}" + echo " - 状态键名: ${{ inputs.state-key }}" + echo " - 缓存键名: ${{ steps.cache-key.outputs.key }}" + echo " - 缓存命中: ${{ steps.cache-get.outputs.cache-hit || steps.cache-set.outputs.cache-hit || 'N/A' }}" + echo " - 使用默认值: ${{ steps.result.outputs.used-default }}" + echo " - 缓存过期: ${{ steps.result.outputs.expired }}" + echo " - 执行删除: ${{ steps.result.outputs.deleted }}" + echo " - 过期时间: ${{ inputs.expiry-seconds }}秒" + + if [[ "${{ steps.result.outputs.used-default }}" == "true" ]]; then + echo " ⚠️ 注意: 使用了默认值,这可能影响后续流程" + fi + + if [[ "${{ steps.result.outputs.expired }}" == "true" ]]; then + echo " ⏰ 注意: 缓存因过期而失效" + fi + + if [[ "${{ steps.result.outputs.deleted }}" == "true" ]]; then + echo " 🗑️ 注意: 已执行删除操作,状态已被清除" + fi diff --git a/cache-state/examples/basic-usage.yml b/cache-state/examples/basic-usage.yml new file mode 100644 index 0000000..4406f5c --- /dev/null +++ b/cache-state/examples/basic-usage.yml @@ -0,0 +1,77 @@ +# 基础使用示例 +# 演示如何使用 cache-state action 进行基本的状态管理 + +name: Cache State - 基础使用示例 + +on: + workflow_dispatch: + inputs: + version: + description: '要设置的版本号' + required: false + default: 'v1.0.0' + +jobs: + demo-basic-usage: + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 获取或设置应用版本 + id: app-version + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'application-version' + state-value: ${{ github.event.inputs.version }} + default-value: 'v0.1.0' + action: 'get-or-set' + + - name: 显示版本信息 + run: | + echo "🏷️ 应用版本: ${{ steps.app-version.outputs.state-value }}" + echo "💾 缓存命中: ${{ steps.app-version.outputs.cache-hit }}" + echo "🔄 使用默认值: ${{ steps.app-version.outputs.used-default }}" + echo "🔑 缓存键: ${{ steps.app-version.outputs.cache-key }}" + + - name: 基于版本信息的条件操作 + run: | + if [[ "${{ steps.app-version.outputs.used-default }}" == "true" ]]; then + echo "⚠️ 使用了默认版本,可能是首次运行" + echo "执行初始化操作..." + else + echo "✅ 使用了已有版本或新设置的版本" + echo "执行常规操作..." + fi + + - name: 记录构建状态 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'last-build-status' + state-value: 'success' + action: 'set' + + - name: 获取构建历史 + id: build-history + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'build-count' + state-value: '1' + default-value: '0' + action: 'get-or-set' + + - name: 更新构建计数 + run: | + CURRENT_COUNT=${{ steps.build-history.outputs.state-value }} + NEW_COUNT=$((CURRENT_COUNT + 1)) + echo "当前构建次数: ${CURRENT_COUNT}" + echo "新的构建次数: ${NEW_COUNT}" + + - name: 保存新的构建计数 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'build-count' + state-value: ${{ env.NEW_COUNT }} + action: 'set' + env: + NEW_COUNT: ${{ steps.build-history.outputs.state-value == '0' && '1' || format('{0}', steps.build-history.outputs.state-value + 1) }} diff --git a/cache-state/examples/delete-demo.yml b/cache-state/examples/delete-demo.yml new file mode 100644 index 0000000..a4a7059 --- /dev/null +++ b/cache-state/examples/delete-demo.yml @@ -0,0 +1,263 @@ +# 删除功能演示 +# 展示如何使用 cache-state action 的删除功能 + +name: Cache State - 删除功能演示 + +on: + workflow_dispatch: + inputs: + demo_type: + description: '演示类型' + required: true + default: 'basic' + type: choice + options: + - basic + - cleanup + - conditional + - batch + +jobs: + demo-basic-delete: + if: ${{ inputs.demo_type == 'basic' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 设置测试状态 + id: setup-state + uses: actions/xgj/cache-state@v1 + with: + state-key: "demo-delete-test" + state-value: "test-value-${{ github.run_id }}" + action: "set" + + - name: 验证状态已设置 + id: verify-set + uses: actions/xgj/cache-state@v1 + with: + state-key: "demo-delete-test" + default-value: "not-found" + action: "get" + + - name: 显示设置结果 + run: | + echo "设置的值: ${{ steps.verify-set.outputs.state-value }}" + echo "缓存命中: ${{ steps.verify-set.outputs.cache-hit }}" + + if [[ "${{ steps.verify-set.outputs.cache-hit }}" == "true" ]]; then + echo "✅ 状态设置成功" + else + echo "❌ 状态设置失败" + exit 1 + fi + + - name: 删除状态 + id: delete-state + uses: actions/xgj/cache-state@v1 + with: + state-key: "demo-delete-test" + action: "del" + + - name: 验证删除结果 + run: | + echo "删除操作: ${{ steps.delete-state.outputs.deleted }}" + echo "缓存键: ${{ steps.delete-state.outputs.cache-key }}" + + if [[ "${{ steps.delete-state.outputs.deleted }}" == "true" ]]; then + echo "✅ 删除操作执行成功" + else + echo "❌ 删除操作失败" + exit 1 + fi + + - name: 验证状态已删除 + id: verify-deleted + uses: actions/xgj/cache-state@v1 + with: + state-key: "demo-delete-test" + default-value: "fallback-value" + action: "get" + + - name: 确认删除效果 + run: | + echo "验证获取的值: ${{ steps.verify-deleted.outputs.state-value }}" + echo "缓存命中: ${{ steps.verify-deleted.outputs.cache-hit }}" + echo "使用默认值: ${{ steps.verify-deleted.outputs.used-default }}" + + if [[ "${{ steps.verify-deleted.outputs.cache-hit }}" == "false" && "${{ steps.verify-deleted.outputs.used-default }}" == "true" ]]; then + echo "✅ 状态已成功删除,使用默认值" + else + echo "❌ 状态删除验证失败" + exit 1 + fi + + demo-cleanup-workflow: + if: ${{ inputs.demo_type == 'cleanup' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 创建多个测试状态 + run: | + echo "创建多个测试状态用于清理演示..." + + - name: 设置构建状态 + uses: actions/xgj/cache-state@v1 + with: + state-key: "build-status-${{ github.run_id }}" + state-value: "completed" + action: "set" + + - name: 设置部署状态 + uses: actions/xgj/cache-state@v1 + with: + state-key: "deploy-status-${{ github.run_id }}" + state-value: "success" + action: "set" + + - name: 设置测试状态 + uses: actions/xgj/cache-state@v1 + with: + state-key: "test-status-${{ github.run_id }}" + state-value: "passed" + action: "set" + + - name: 模拟工作流结束后的清理 + run: | + echo "🧹 开始清理工作流状态..." + + - name: 清理构建状态 + uses: actions/xgj/cache-state@v1 + with: + state-key: "build-status-${{ github.run_id }}" + action: "del" + + - name: 清理部署状态 + uses: actions/xgj/cache-state@v1 + with: + state-key: "deploy-status-${{ github.run_id }}" + action: "del" + + - name: 清理测试状态 + uses: actions/xgj/cache-state@v1 + with: + state-key: "test-status-${{ github.run_id }}" + action: "del" + + - name: 清理完成 + run: | + echo "✅ 所有状态已清理完成" + + demo-conditional-delete: + if: ${{ inputs.demo_type == 'conditional' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 检查现有状态 + id: check-state + uses: actions/xgj/cache-state@v1 + with: + state-key: "conditional-delete-test" + default-value: "none" + action: "get" + + - name: 显示当前状态 + run: | + echo "当前状态: ${{ steps.check-state.outputs.state-value }}" + echo "缓存命中: ${{ steps.check-state.outputs.cache-hit }}" + + - name: 条件性删除 - 如果状态存在 + if: ${{ steps.check-state.outputs.cache-hit == 'true' }} + id: conditional-delete + uses: actions/xgj/cache-state@v1 + with: + state-key: "conditional-delete-test" + action: "del" + + - name: 条件性设置 - 如果状态不存在 + if: ${{ steps.check-state.outputs.cache-hit != 'true' }} + uses: actions/xgj/cache-state@v1 + with: + state-key: "conditional-delete-test" + state-value: "newly-created-${{ github.run_id }}" + action: "set" + + - name: 显示操作结果 + run: | + if [[ "${{ steps.check-state.outputs.cache-hit }}" == "true" ]]; then + echo "🗑️ 执行了删除操作" + echo "删除结果: ${{ steps.conditional-delete.outputs.deleted }}" + else + echo "🆕 执行了创建操作" + fi + + demo-batch-operations: + if: ${{ inputs.demo_type == 'batch' }} + runs-on: ubuntu-latest + strategy: + matrix: + operation: [create, delete] + state_id: [1, 2, 3] + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 批量创建状态 + if: ${{ matrix.operation == 'create' }} + uses: actions/xgj/cache-state@v1 + with: + state-key: "batch-state-${{ matrix.state_id }}" + state-value: "batch-value-${{ matrix.state_id }}-${{ github.run_id }}" + action: "set" + + - name: 批量删除状态 + if: ${{ matrix.operation == 'delete' }} + uses: actions/xgj/cache-state@v1 + with: + state-key: "batch-state-${{ matrix.state_id }}" + action: "del" + + - name: 显示操作结果 + run: | + echo "操作: ${{ matrix.operation }}" + echo "状态ID: ${{ matrix.state_id }}" + echo "✅ 批量操作 ${{ matrix.operation }} 完成" + + demo-summary: + needs: [demo-basic-delete, demo-cleanup-workflow, demo-conditional-delete, demo-batch-operations] + if: always() + runs-on: ubuntu-latest + steps: + - name: 生成演示总结 + run: | + echo "## 🗑️ 删除功能演示总结" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 演示类型: ${{ inputs.demo_type }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + case "${{ inputs.demo_type }}" in + "basic") + echo "✅ **基础删除演示**: 展示了设置、删除、验证的完整流程" >> $GITHUB_STEP_SUMMARY + ;; + "cleanup") + echo "✅ **清理工作流演示**: 展示了批量清理多个状态的用法" >> $GITHUB_STEP_SUMMARY + ;; + "conditional") + echo "✅ **条件删除演示**: 展示了基于状态存在性的条件操作" >> $GITHUB_STEP_SUMMARY + ;; + "batch") + echo "✅ **批量操作演示**: 展示了并行批量创建和删除状态" >> $GITHUB_STEP_SUMMARY + ;; + esac + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 核心特性" >> $GITHUB_STEP_SUMMARY + echo "- 🗑️ **状态删除**: 通过删除标记实现状态清除" >> $GITHUB_STEP_SUMMARY + echo "- 🔄 **即时生效**: 删除后立即生效,后续访问使用默认值" >> $GITHUB_STEP_SUMMARY + echo "- 📊 **状态透明**: 通过 \`deleted\` 输出明确告知删除操作" >> $GITHUB_STEP_SUMMARY + echo "- 🛡️ **安全可靠**: 基于缓存覆盖机制,不会影响其他状态" >> $GITHUB_STEP_SUMMARY diff --git a/cache-state/examples/deployment-state.yml b/cache-state/examples/deployment-state.yml new file mode 100644 index 0000000..50b9f7c --- /dev/null +++ b/cache-state/examples/deployment-state.yml @@ -0,0 +1,200 @@ +# 部署状态管理示例 +# 演示如何在部署工作流中使用状态缓存来跟踪部署进度和状态 + +name: Cache State - 部署状态管理 + +on: + push: + branches: [main, develop] + workflow_dispatch: + inputs: + environment: + description: '部署环境' + required: true + default: 'staging' + type: choice + options: + - staging + - production + force_deploy: + description: '强制部署(忽略上次部署状态)' + required: false + default: false + type: boolean + +jobs: + check-deployment-state: + runs-on: ubuntu-latest + outputs: + should-deploy: ${{ steps.deploy-decision.outputs.should-deploy }} + last-deployed-sha: ${{ steps.last-deploy.outputs.state-value }} + environment: ${{ steps.env-state.outputs.state-value }} + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 获取环境配置 + id: env-state + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'target-environment-${{ github.ref_name }}' + state-value: ${{ github.event.inputs.environment || 'staging' }} + default-value: 'staging' + cache-prefix: 'deploy-env' + + - name: 检查上次部署的SHA + id: last-deploy + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'last-deployed-sha-${{ steps.env-state.outputs.state-value }}' + default-value: 'none' + action: 'get' + + - name: 检查部署状态 + id: deploy-status + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deploy-status-${{ steps.env-state.outputs.state-value }}' + default-value: 'idle' + action: 'get' + + - name: 决定是否需要部署 + id: deploy-decision + run: | + FORCE_DEPLOY="${{ github.event.inputs.force_deploy }}" + LAST_SHA="${{ steps.last-deploy.outputs.state-value }}" + CURRENT_SHA="${{ github.sha }}" + DEPLOY_STATUS="${{ steps.deploy-status.outputs.state-value }}" + + echo "🔍 部署决策分析:" + echo " - 强制部署: ${FORCE_DEPLOY}" + echo " - 上次部署SHA: ${LAST_SHA}" + echo " - 当前SHA: ${CURRENT_SHA}" + echo " - 部署状态: ${DEPLOY_STATUS}" + + if [[ "${FORCE_DEPLOY}" == "true" ]]; then + echo "✅ 强制部署模式,将执行部署" + echo "should-deploy=true" >> $GITHUB_OUTPUT + elif [[ "${DEPLOY_STATUS}" == "deploying" ]]; then + echo "⚠️ 检测到正在进行的部署,跳过" + echo "should-deploy=false" >> $GITHUB_OUTPUT + elif [[ "${LAST_SHA}" != "${CURRENT_SHA}" || "${LAST_SHA}" == "none" ]]; then + echo "✅ 检测到新的变更,需要部署" + echo "should-deploy=true" >> $GITHUB_OUTPUT + else + echo "ℹ️ 没有新的变更,跳过部署" + echo "should-deploy=false" >> $GITHUB_OUTPUT + fi + + deploy: + needs: check-deployment-state + if: needs.check-deployment-state.outputs.should-deploy == 'true' + runs-on: ubuntu-latest + environment: ${{ needs.check-deployment-state.outputs.environment }} + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 标记部署开始 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deploy-status-${{ needs.check-deployment-state.outputs.environment }}' + state-value: 'deploying' + action: 'set' + + - name: 记录部署开始时间 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deploy-start-time-${{ needs.check-deployment-state.outputs.environment }}' + state-value: ${{ github.run_started_at }} + action: 'set' + + - name: 模拟部署过程 + run: | + echo "🚀 开始部署到 ${{ needs.check-deployment-state.outputs.environment }} 环境..." + echo "📦 部署版本: ${{ github.sha }}" + + # 模拟部署步骤 + for i in {1..5}; do + echo " 步骤 ${i}/5: 部署进行中..." + sleep 2 + done + + echo "✅ 部署完成!" + + - name: 记录成功部署的SHA + if: success() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'last-deployed-sha-${{ needs.check-deployment-state.outputs.environment }}' + state-value: ${{ github.sha }} + action: 'set' + + - name: 标记部署成功 + if: success() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deploy-status-${{ needs.check-deployment-state.outputs.environment }}' + state-value: 'success' + action: 'set' + + - name: 记录部署完成时间 + if: success() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deploy-end-time-${{ needs.check-deployment-state.outputs.environment }}' + state-value: ${{ github.event.head_commit.timestamp }} + action: 'set' + + - name: 标记部署失败 + if: failure() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deploy-status-${{ needs.check-deployment-state.outputs.environment }}' + state-value: 'failed' + action: 'set' + + - name: 记录失败信息 + if: failure() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deploy-error-${{ needs.check-deployment-state.outputs.environment }}' + state-value: 'Deployment failed at ${{ github.event.head_commit.timestamp }}' + action: 'set' + + post-deploy: + needs: [check-deployment-state, deploy] + if: always() && needs.check-deployment-state.outputs.should-deploy == 'true' + runs-on: ubuntu-latest + steps: + - name: 获取最终部署状态 + id: final-status + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deploy-status-${{ needs.check-deployment-state.outputs.environment }}' + default-value: 'unknown' + action: 'get' + + - name: 部署状态总结 + run: | + echo "📊 部署状态总结:" + echo " - 环境: ${{ needs.check-deployment-state.outputs.environment }}" + echo " - 最终状态: ${{ steps.final-status.outputs.state-value }}" + echo " - 部署SHA: ${{ github.sha }}" + + if [[ "${{ steps.final-status.outputs.state-value }}" == "success" ]]; then + echo "🎉 部署成功完成!" + elif [[ "${{ steps.final-status.outputs.state-value }}" == "failed" ]]; then + echo "❌ 部署失败,请检查日志" + else + echo "⚠️ 部署状态未知" + fi + + # 清理部署锁定状态(如果需要) + - name: 清理部署锁定 + if: always() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deploy-status-${{ needs.check-deployment-state.outputs.environment }}' + state-value: 'idle' + action: 'set' diff --git a/cache-state/examples/error-handling.yml b/cache-state/examples/error-handling.yml new file mode 100644 index 0000000..9bd775a --- /dev/null +++ b/cache-state/examples/error-handling.yml @@ -0,0 +1,377 @@ +# 错误处理和恢复示例 +# 演示如何使用状态缓存进行错误处理、状态恢复和重试逻辑 + +name: Cache State - 错误处理示例 + +on: + workflow_dispatch: + inputs: + simulate_error: + description: '模拟错误类型' + required: false + default: 'none' + type: choice + options: + - none + - network_error + - build_failure + - deployment_error + recovery_mode: + description: '恢复模式' + required: false + default: 'auto' + type: choice + options: + - auto + - manual + - skip + +jobs: + initialize-operation: + runs-on: ubuntu-latest + outputs: + operation-id: ${{ steps.operation-setup.outputs.operation-id }} + should-continue: ${{ steps.check-previous.outputs.should-continue }} + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 设置操作ID + id: operation-setup + run: | + OPERATION_ID="op-$(date +%s)-${{ github.run_number }}" + echo "operation-id=${OPERATION_ID}" >> $GITHUB_OUTPUT + echo "🆔 操作ID: ${OPERATION_ID}" + + - name: 检查上次操作状态 + id: last-operation + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'last-operation-status' + default-value: 'none' + action: 'get' + cache-prefix: 'operation' + + - name: 检查是否有失败的操作需要恢复 + id: check-previous + run: | + LAST_STATUS="${{ steps.last-operation.outputs.state-value }}" + RECOVERY_MODE="${{ github.event.inputs.recovery_mode }}" + + echo "🔍 检查上次操作状态: ${LAST_STATUS}" + echo "🔧 恢复模式: ${RECOVERY_MODE}" + + if [[ "${LAST_STATUS}" == "failed" || "${LAST_STATUS}" == "partial" ]]; then + echo "⚠️ 检测到失败的操作需要处理" + + case "${RECOVERY_MODE}" in + "auto") + echo "🔄 自动恢复模式,将继续操作" + echo "should-continue=true" >> $GITHUB_OUTPUT + ;; + "manual") + echo "✋ 手动恢复模式,需要人工干预" + echo "should-continue=false" >> $GITHUB_OUTPUT + exit 1 + ;; + "skip") + echo "⏭️ 跳过恢复,开始新操作" + echo "should-continue=true" >> $GITHUB_OUTPUT + ;; + esac + else + echo "✅ 没有失败的操作,正常继续" + echo "should-continue=true" >> $GITHUB_OUTPUT + fi + + - name: 记录操作开始 + if: steps.check-previous.outputs.should-continue == 'true' + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'current-operation-id' + state-value: ${{ steps.operation-setup.outputs.operation-id }} + action: 'set' + cache-prefix: 'operation' + + - name: 记录操作状态为进行中 + if: steps.check-previous.outputs.should-continue == 'true' + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'last-operation-status' + state-value: 'running' + action: 'set' + cache-prefix: 'operation' + + build-with-retry: + needs: initialize-operation + if: needs.initialize-operation.outputs.should-continue == 'true' + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 检查构建重试状态 + id: retry-status + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'build-retry-count-${{ needs.initialize-operation.outputs.operation-id }}' + default-value: '0' + action: 'get' + cache-prefix: 'retry' + + - name: 准备构建重试 + id: prepare-retry + run: | + CURRENT_RETRY=${{ steps.retry-status.outputs.state-value }} + MAX_RETRIES=3 + + echo "🔄 当前重试次数: ${CURRENT_RETRY}/${MAX_RETRIES}" + + if [[ ${CURRENT_RETRY} -ge ${MAX_RETRIES} ]]; then + echo "❌ 已达到最大重试次数" + echo "can-retry=false" >> $GITHUB_OUTPUT + exit 1 + else + echo "✅ 可以继续重试" + echo "can-retry=true" >> $GITHUB_OUTPUT + echo "next-retry=$((CURRENT_RETRY + 1))" >> $GITHUB_OUTPUT + fi + + - name: 模拟构建过程 + id: build + run: | + echo "🔨 开始构建过程..." + + # 根据输入模拟不同类型的错误 + case "${{ github.event.inputs.simulate_error }}" in + "build_failure") + echo "❌ 模拟构建失败" + exit 1 + ;; + "network_error") + echo "🌐 模拟网络错误" + sleep 2 + exit 1 + ;; + *) + echo "✅ 构建成功" + ;; + esac + + - name: 更新重试计数(失败时) + if: failure() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'build-retry-count-${{ needs.initialize-operation.outputs.operation-id }}' + state-value: ${{ steps.prepare-retry.outputs.next-retry }} + action: 'set' + cache-prefix: 'retry' + + - name: 记录构建失败状态 + if: failure() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'build-status-${{ needs.initialize-operation.outputs.operation-id }}' + state-value: 'failed' + action: 'set' + cache-prefix: 'build' + + - name: 记录构建失败时间 + if: failure() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'build-failure-time-${{ needs.initialize-operation.outputs.operation-id }}' + state-value: ${{ github.event.head_commit.timestamp }} + action: 'set' + cache-prefix: 'build' + + - name: 清理重试计数(成功时) + if: success() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'build-retry-count-${{ needs.initialize-operation.outputs.operation-id }}' + state-value: '' + action: 'set' + cache-prefix: 'retry' + + - name: 记录构建成功状态 + if: success() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'build-status-${{ needs.initialize-operation.outputs.operation-id }}' + state-value: 'success' + action: 'set' + cache-prefix: 'build' + + deploy-with-rollback: + needs: [initialize-operation, build-with-retry] + if: success() + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 获取上次成功的部署版本 + id: last-deploy + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'last-successful-deployment' + default-value: 'v1.0.0' + action: 'get' + cache-prefix: 'deploy' + + - name: 备份当前部署状态 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deployment-backup-${{ needs.initialize-operation.outputs.operation-id }}' + state-value: ${{ steps.last-deploy.outputs.state-value }} + action: 'set' + cache-prefix: 'backup' + + - name: 模拟部署过程 + id: deploy + run: | + echo "🚀 开始部署..." + echo "📦 部署版本: ${{ github.sha }}" + echo "🔙 备份版本: ${{ steps.last-deploy.outputs.state-value }}" + + # 模拟部署错误 + if [[ "${{ github.event.inputs.simulate_error }}" == "deployment_error" ]]; then + echo "❌ 模拟部署失败" + exit 1 + fi + + echo "✅ 部署成功" + + - name: 记录部署成功 + if: success() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'last-successful-deployment' + state-value: ${{ github.sha }} + action: 'set' + cache-prefix: 'deploy' + + - name: 清理备份(成功时) + if: success() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'deployment-backup-${{ needs.initialize-operation.outputs.operation-id }}' + state-value: '' + action: 'set' + cache-prefix: 'backup' + + - name: 执行回滚(失败时) + if: failure() + run: | + echo "🔄 部署失败,开始回滚..." + ROLLBACK_VERSION="${{ steps.last-deploy.outputs.state-value }}" + echo "回滚到版本: ${ROLLBACK_VERSION}" + + # 这里执行实际的回滚操作 + echo "✅ 回滚完成" + + - name: 记录回滚状态 + if: failure() + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'rollback-executed-${{ needs.initialize-operation.outputs.operation-id }}' + state-value: 'true' + action: 'set' + cache-prefix: 'rollback' + + finalize-operation: + needs: [initialize-operation, build-with-retry, deploy-with-rollback] + if: always() && needs.initialize-operation.outputs.should-continue == 'true' + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 确定最终操作状态 + id: final-status + run: | + BUILD_RESULT="${{ needs.build-with-retry.result }}" + DEPLOY_RESULT="${{ needs.deploy-with-rollback.result }}" + + echo "🔍 构建结果: ${BUILD_RESULT}" + echo "🔍 部署结果: ${DEPLOY_RESULT}" + + if [[ "${BUILD_RESULT}" == "success" && "${DEPLOY_RESULT}" == "success" ]]; then + FINAL_STATUS="success" + elif [[ "${BUILD_RESULT}" == "failure" ]]; then + FINAL_STATUS="build_failed" + elif [[ "${DEPLOY_RESULT}" == "failure" ]]; then + FINAL_STATUS="deploy_failed" + elif [[ "${BUILD_RESULT}" == "success" && "${DEPLOY_RESULT}" == "skipped" ]]; then + FINAL_STATUS="partial" + else + FINAL_STATUS="unknown" + fi + + echo "final-status=${FINAL_STATUS}" >> $GITHUB_OUTPUT + echo "🏁 最终状态: ${FINAL_STATUS}" + + - name: 记录最终操作状态 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'last-operation-status' + state-value: ${{ steps.final-status.outputs.final-status }} + action: 'set' + cache-prefix: 'operation' + + - name: 记录操作完成时间 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'last-operation-time' + state-value: ${{ github.event.head_commit.timestamp }} + action: 'set' + cache-prefix: 'operation' + + - name: 生成错误处理报告 + run: | + echo "📋 错误处理和恢复报告" + echo "========================" + echo "操作ID: ${{ needs.initialize-operation.outputs.operation-id }}" + echo "最终状态: ${{ steps.final-status.outputs.final-status }}" + echo "模拟错误类型: ${{ github.event.inputs.simulate_error }}" + echo "恢复模式: ${{ github.event.inputs.recovery_mode }}" + echo "" + + case "${{ steps.final-status.outputs.final-status }}" in + "success") + echo "✅ 操作成功完成" + ;; + "build_failed") + echo "❌ 构建失败,请检查构建日志" + ;; + "deploy_failed") + echo "❌ 部署失败,已执行回滚" + ;; + "partial") + echo "⚠️ 部分完成,可能需要手动干预" + ;; + *) + echo "❓ 状态未知,需要进一步调查" + ;; + esac + + cleanup-expired-states: + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 清理过期的重试状态 + run: | + echo "🧹 清理过期的状态信息..." + # 这里可以实现清理逻辑 + # 例如,清理超过一定时间的重试计数、备份等临时状态 + + - name: 清理示例 - 重置错误计数 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'error-count-global' + state-value: '0' + action: 'set' + cache-prefix: 'cleanup' diff --git a/cache-state/examples/expiry-demo.yml b/cache-state/examples/expiry-demo.yml new file mode 100644 index 0000000..f6d6f9b --- /dev/null +++ b/cache-state/examples/expiry-demo.yml @@ -0,0 +1,243 @@ +# 过期时间功能演示 +# 展示如何使用 cache-state action 的过期时间功能 + +name: Cache State - 过期时间演示 + +on: + workflow_dispatch: + inputs: + demo_type: + description: '演示类型' + required: true + default: 'basic' + type: choice + options: + - basic + - session + - build-lock + - rate-limit + +jobs: + demo-basic-expiry: + if: ${{ inputs.demo_type == 'basic' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 设置短期缓存(5分钟过期) + id: short-cache + uses: actions/xgj/cache-state@v1 + with: + state-key: "demo-short-cache" + state-value: "cached-at-${{ github.run_started_at }}" + default-value: "no-cache" + expiry-seconds: "300" # 5分钟 + action: "get-or-set" + + - name: 显示缓存状态 + run: | + echo "缓存值: ${{ steps.short-cache.outputs.state-value }}" + echo "缓存命中: ${{ steps.short-cache.outputs.cache-hit }}" + echo "缓存过期: ${{ steps.short-cache.outputs.expired }}" + echo "使用默认值: ${{ steps.short-cache.outputs.used-default }}" + + - name: 等待1分钟 + run: sleep 60 + + - name: 再次获取缓存(应该仍然有效) + id: check-cache + uses: actions/xgj/cache-state@v1 + with: + state-key: "demo-short-cache" + default-value: "expired" + expiry-seconds: "300" + action: "get" + + - name: 验证缓存仍然有效 + run: | + echo "再次获取的值: ${{ steps.check-cache.outputs.state-value }}" + echo "缓存命中: ${{ steps.check-cache.outputs.cache-hit }}" + + if [[ "${{ steps.check-cache.outputs.cache-hit }}" == "true" ]]; then + echo "✅ 缓存在1分钟后仍然有效" + else + echo "❌ 缓存意外失效" + fi + + demo-session-management: + if: ${{ inputs.demo_type == 'session' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 创建用户会话(1小时过期) + id: user-session + uses: actions/xgj/cache-state@v1 + with: + state-key: "user-session-${{ github.actor }}" + state-value: "session-${{ github.run_id }}" + default-value: "no-session" + expiry-seconds: "3600" # 1小时 + action: "get-or-set" + + - name: 处理会话状态 + run: | + SESSION_VALUE="${{ steps.user-session.outputs.state-value }}" + + echo "用户: ${{ github.actor }}" + echo "会话状态: ${SESSION_VALUE}" + echo "缓存命中: ${{ steps.user-session.outputs.cache-hit }}" + echo "缓存过期: ${{ steps.user-session.outputs.expired }}" + + if [[ "${{ steps.user-session.outputs.expired }}" == "true" ]]; then + echo "⏰ 会话已过期,创建新会话" + elif [[ "${{ steps.user-session.outputs.cache-hit }}" == "true" ]]; then + echo "✅ 会话仍然有效,继续使用" + else + echo "🆕 首次创建会话" + fi + + - name: 模拟会话活动 + run: | + echo "执行一些需要会话的操作..." + echo "会话ID: ${{ steps.user-session.outputs.state-value }}" + + demo-build-lock: + if: ${{ inputs.demo_type == 'build-lock' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 检查构建锁定状态(10分钟过期) + id: build-lock + uses: actions/xgj/cache-state@v1 + with: + state-key: "build-lock-${{ github.repository }}" + state-value: "locked-by-${{ github.run_id }}" + default-value: "unlocked" + expiry-seconds: "600" # 10分钟 + action: "get-or-set" + + - name: 处理构建锁定 + run: | + LOCK_VALUE="${{ steps.build-lock.outputs.state-value }}" + + echo "构建锁定状态: ${LOCK_VALUE}" + echo "缓存命中: ${{ steps.build-lock.outputs.cache-hit }}" + echo "缓存过期: ${{ steps.build-lock.outputs.expired }}" + + if [[ "${LOCK_VALUE}" == "unlocked" ]]; then + echo "✅ 没有构建锁定,可以开始构建" + elif [[ "${LOCK_VALUE}" == "locked-by-${{ github.run_id }}" ]]; then + echo "✅ 当前运行获得了构建锁" + elif [[ "${{ steps.build-lock.outputs.expired }}" == "true" ]]; then + echo "⏰ 构建锁已过期,获得新锁" + else + echo "⚠️ 构建被其他运行锁定: ${LOCK_VALUE}" + echo "等待锁定过期或手动解锁" + exit 1 + fi + + - name: 模拟构建过程 + run: | + echo "🔨 开始构建..." + sleep 30 + echo "✅ 构建完成" + + - name: 释放构建锁 + if: always() + uses: actions/xgj/cache-state@v1 + with: + state-key: "build-lock-${{ github.repository }}" + state-value: "unlocked" + expiry-seconds: "600" + action: "set" + + demo-rate-limiting: + if: ${{ inputs.demo_type == 'rate-limit' }} + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 检查API调用限制(1分钟窗口) + id: api-limit + uses: actions/xgj/cache-state@v1 + with: + state-key: "api-calls-${{ github.actor }}" + state-value: "1" + default-value: "0" + expiry-seconds: "60" # 1分钟窗口 + action: "get-or-set" + + - name: 处理速率限制 + run: | + CALL_COUNT="${{ steps.api-limit.outputs.state-value }}" + MAX_CALLS=5 + + echo "当前调用次数: ${CALL_COUNT}/${MAX_CALLS}" + echo "缓存命中: ${{ steps.api-limit.outputs.cache-hit }}" + echo "缓存过期: ${{ steps.api-limit.outputs.expired }}" + + if [[ "${{ steps.api-limit.outputs.expired }}" == "true" ]]; then + echo "⏰ 速率限制窗口已重置" + fi + + if [[ ${CALL_COUNT} -le ${MAX_CALLS} ]]; then + echo "✅ 在速率限制内,可以继续调用" + else + echo "❌ 超出速率限制,请等待" + exit 1 + fi + + - name: 模拟API调用 + run: | + echo "📡 调用API..." + sleep 5 + echo "✅ API调用成功" + + - name: 更新调用计数 + if: success() + uses: actions/xgj/cache-state@v1 + with: + state-key: "api-calls-${{ github.actor }}" + state-value: "${{ steps.api-limit.outputs.state-value + 1 }}" + expiry-seconds: "60" + action: "set" + + demo-summary: + needs: [demo-basic-expiry, demo-session-management, demo-build-lock, demo-rate-limiting] + if: always() + runs-on: ubuntu-latest + steps: + - name: 生成演示总结 + run: | + echo "## 🎯 过期时间功能演示总结" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 演示类型: ${{ inputs.demo_type }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + case "${{ inputs.demo_type }}" in + "basic") + echo "✅ **基础过期演示**: 展示了5分钟过期缓存的基本用法" >> $GITHUB_STEP_SUMMARY + ;; + "session") + echo "✅ **会话管理演示**: 展示了1小时过期的用户会话管理" >> $GITHUB_STEP_SUMMARY + ;; + "build-lock") + echo "✅ **构建锁定演示**: 展示了10分钟过期的构建锁定机制" >> $GITHUB_STEP_SUMMARY + ;; + "rate-limit") + echo "✅ **速率限制演示**: 展示了1分钟窗口的API调用限制" >> $GITHUB_STEP_SUMMARY + ;; + esac + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### 核心特性" >> $GITHUB_STEP_SUMMARY + echo "- ⏰ **时间窗口过期**: 基于时间窗口的自动过期机制" >> $GITHUB_STEP_SUMMARY + echo "- 🔄 **简单实现**: 不依赖复杂工具,使用基础shell命令" >> $GITHUB_STEP_SUMMARY + echo "- 📊 **状态透明**: 清晰显示缓存命中、过期状态" >> $GITHUB_STEP_SUMMARY + echo "- 🛡️ **安全可靠**: 利用GitHub Actions缓存的天然特性" >> $GITHUB_STEP_SUMMARY diff --git a/cache-state/examples/feature-flags.yml b/cache-state/examples/feature-flags.yml new file mode 100644 index 0000000..616c8ca --- /dev/null +++ b/cache-state/examples/feature-flags.yml @@ -0,0 +1,326 @@ +# 功能开关状态管理示例 +# 演示如何使用状态缓存来管理功能开关、配置参数等动态配置 + +name: Cache State - 功能开关管理 + +on: + workflow_dispatch: + inputs: + feature_name: + description: '功能名称' + required: true + default: 'new-ui' + feature_enabled: + description: '是否启用功能' + required: false + default: true + type: boolean + rollout_percentage: + description: '灰度发布百分比 (0-100)' + required: false + default: '50' + environment: + description: '目标环境' + required: false + default: 'staging' + type: choice + options: + - development + - staging + - production + +jobs: + manage-feature-flags: + runs-on: ubuntu-latest + outputs: + feature-state: ${{ steps.feature-flag.outputs.state-value }} + rollout-config: ${{ steps.rollout.outputs.state-value }} + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 获取或设置功能开关状态 + id: feature-flag + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'feature-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + state-value: ${{ github.event.inputs.feature_enabled }} + default-value: 'false' + cache-prefix: 'feature-flags' + action: 'get-or-set' + + - name: 管理灰度发布配置 + id: rollout + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'rollout-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + state-value: ${{ github.event.inputs.rollout_percentage }} + default-value: '0' + cache-prefix: 'rollout-config' + action: 'get-or-set' + + - name: 记录功能开关变更历史 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'feature-history-${{ github.event.inputs.feature_name }}' + state-value: '${{ github.event.inputs.environment }}:${{ github.event.inputs.feature_enabled }}:${{ github.run_started_at }}' + cache-prefix: 'feature-history' + action: 'set' + + - name: 验证功能开关配置 + run: | + FEATURE_ENABLED="${{ steps.feature-flag.outputs.state-value }}" + ROLLOUT_PERCENT="${{ steps.rollout.outputs.state-value }}" + + echo "🎛️ 功能开关配置验证:" + echo " - 功能名称: ${{ github.event.inputs.feature_name }}" + echo " - 环境: ${{ github.event.inputs.environment }}" + echo " - 功能状态: ${FEATURE_ENABLED}" + echo " - 灰度百分比: ${ROLLOUT_PERCENT}%" + echo " - 缓存命中: ${{ steps.feature-flag.outputs.cache-hit }}" + + # 验证配置合理性 + if [[ "${FEATURE_ENABLED}" == "true" && "${ROLLOUT_PERCENT}" -gt 0 ]]; then + echo "✅ 功能配置有效" + elif [[ "${FEATURE_ENABLED}" == "false" ]]; then + echo "ℹ️ 功能已禁用" + else + echo "⚠️ 功能配置可能需要检查" + fi + + apply-feature-configuration: + needs: manage-feature-flags + runs-on: ubuntu-latest + strategy: + matrix: + service: [api, frontend, mobile] + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 获取服务特定的功能配置 + id: service-config + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'service-config-${{ matrix.service }}-${{ github.event.inputs.environment }}' + default-value: '{"features":{},"version":"1.0"}' + action: 'get' + cache-prefix: 'service-config' + + - name: 应用功能配置到服务 + run: | + SERVICE="${{ matrix.service }}" + FEATURE_NAME="${{ github.event.inputs.feature_name }}" + FEATURE_ENABLED="${{ needs.manage-feature-flags.outputs.feature-state }}" + ROLLOUT_PERCENT="${{ needs.manage-feature-flags.outputs.rollout-config }}" + + echo "🔧 为 ${SERVICE} 服务应用功能配置:" + echo " - 功能: ${FEATURE_NAME}" + echo " - 状态: ${FEATURE_ENABLED}" + echo " - 灰度: ${ROLLOUT_PERCENT}%" + + # 模拟配置应用过程 + case "${SERVICE}" in + "api") + echo " 📡 API服务配置已更新" + ;; + "frontend") + echo " 🌐 前端服务配置已更新" + ;; + "mobile") + echo " 📱 移动端配置已更新" + ;; + esac + + - name: 更新服务配置状态 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'service-config-applied-${{ matrix.service }}' + state-value: '${{ github.event.inputs.feature_name }}:${{ needs.manage-feature-flags.outputs.feature-state }}' + cache-prefix: 'service-status' + action: 'set' + + monitor-feature-metrics: + needs: [manage-feature-flags, apply-feature-configuration] + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 初始化功能监控指标 + id: init-metrics + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'metrics-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + default-value: '{"users":0,"errors":0,"performance":100}' + action: 'get' + cache-prefix: 'feature-metrics' + + - name: 模拟收集功能使用指标 + run: | + echo "📊 收集功能使用指标..." + + # 模拟指标收集 + USERS_COUNT=$((RANDOM % 1000 + 100)) + ERROR_COUNT=$((RANDOM % 10)) + PERFORMANCE_SCORE=$((RANDOM % 20 + 80)) + + echo " - 用户数: ${USERS_COUNT}" + echo " - 错误数: ${ERROR_COUNT}" + echo " - 性能分数: ${PERFORMANCE_SCORE}" + + # 保存到环境变量供后续步骤使用 + echo "users_count=${USERS_COUNT}" >> $GITHUB_ENV + echo "error_count=${ERROR_COUNT}" >> $GITHUB_ENV + echo "performance_score=${PERFORMANCE_SCORE}" >> $GITHUB_ENV + + - name: 更新功能监控指标 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'metrics-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + state-value: '{"users":${{ env.users_count }},"errors":${{ env.error_count }},"performance":${{ env.performance_score }}}' + cache-prefix: 'feature-metrics' + action: 'set' + + - name: 分析指标异常 + run: | + ERROR_COUNT=${{ env.error_count }} + PERFORMANCE_SCORE=${{ env.performance_score }} + + echo "🔍 指标异常分析:" + + if [[ ${ERROR_COUNT} -gt 5 ]]; then + echo "⚠️ 错误率偏高,可能需要回滚功能" + echo "alert=high_error_rate" >> $GITHUB_ENV + elif [[ ${PERFORMANCE_SCORE} -lt 70 ]]; then + echo "⚠️ 性能下降,需要关注" + echo "alert=performance_degradation" >> $GITHUB_ENV + else + echo "✅ 指标正常" + echo "alert=none" >> $GITHUB_ENV + fi + + - name: 记录异常告警 + if: env.alert != 'none' + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'alert-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + state-value: '${{ env.alert }}:${{ github.run_started_at }}' + cache-prefix: 'alerts' + action: 'set' + + feature-rollback-if-needed: + needs: [manage-feature-flags, monitor-feature-metrics] + if: always() + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 检查是否需要回滚 + id: check-rollback + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'alert-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + default-value: 'none' + action: 'get' + cache-prefix: 'alerts' + + - name: 执行自动回滚 + if: contains(steps.check-rollback.outputs.state-value, 'high_error_rate') + run: | + echo "🔄 检测到高错误率,执行自动回滚..." + echo "功能: ${{ github.event.inputs.feature_name }}" + echo "环境: ${{ github.event.inputs.environment }}" + + - name: 禁用功能开关 + if: contains(steps.check-rollback.outputs.state-value, 'high_error_rate') + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'feature-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + state-value: 'false' + cache-prefix: 'feature-flags' + action: 'set' + + - name: 记录回滚操作 + if: contains(steps.check-rollback.outputs.state-value, 'high_error_rate') + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'rollback-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + state-value: 'auto-rollback:${{ github.run_started_at }}' + cache-prefix: 'rollback-history' + action: 'set' + + generate-feature-report: + needs: [manage-feature-flags, apply-feature-configuration, monitor-feature-metrics, feature-rollback-if-needed] + if: always() + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 收集所有功能状态 + run: | + echo "📋 功能开关管理报告" + echo "====================" + echo "时间: $(date)" + echo "功能: ${{ github.event.inputs.feature_name }}" + echo "环境: ${{ github.event.inputs.environment }}" + echo "" + + - name: 获取最终功能状态 + id: final-state + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'feature-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + action: 'get' + cache-prefix: 'feature-flags' + + - name: 获取监控指标 + id: final-metrics + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'metrics-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + action: 'get' + cache-prefix: 'feature-metrics' + + - name: 检查回滚历史 + id: rollback-check + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'rollback-${{ github.event.inputs.feature_name }}-${{ github.event.inputs.environment }}' + default-value: 'none' + action: 'get' + cache-prefix: 'rollback-history' + + - name: 生成最终报告 + run: | + echo "✅ 功能状态管理完成" + echo "" + echo "📊 最终状态:" + echo " - 功能状态: ${{ steps.final-state.outputs.state-value }}" + echo " - 监控指标: ${{ steps.final-metrics.outputs.state-value }}" + echo " - 回滚历史: ${{ steps.rollback-check.outputs.state-value }}" + echo "" + echo "🎯 下次运行时,这些状态将被保留并可以继续使用。" + + cleanup-old-feature-states: + runs-on: ubuntu-latest + if: github.event.inputs.environment == 'development' # 只在开发环境清理 + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 清理过期的功能状态 + run: | + echo "🧹 清理开发环境的过期功能状态..." + # 这里可以实现清理逻辑 + # 例如,清理超过一定时间的开发环境功能开关 + + - name: 重置开发环境告警 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'dev-alerts-cleared' + state-value: '${{ github.run_started_at }}' + cache-prefix: 'cleanup' + action: 'set' diff --git a/cache-state/examples/multi-environment.yml b/cache-state/examples/multi-environment.yml new file mode 100644 index 0000000..e8cfd63 --- /dev/null +++ b/cache-state/examples/multi-environment.yml @@ -0,0 +1,290 @@ +# 多环境状态管理示例 +# 演示如何在多环境部署中使用状态缓存来管理不同环境的配置和状态 + +name: Cache State - 多环境管理 + +on: + workflow_dispatch: + inputs: + target_environments: + description: '目标环境(逗号分隔)' + required: true + default: 'dev,staging,production' + config_update: + description: '是否更新配置' + required: false + default: false + type: boolean + +jobs: + setup-environments: + runs-on: ubuntu-latest + strategy: + matrix: + environment: [dev, staging, production] + outputs: + environments: ${{ steps.env-list.outputs.environments }} + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 环境特定配置 + id: env-config + run: | + case "${{ matrix.environment }}" in + "dev") + echo "replicas=1" >> $GITHUB_OUTPUT + echo "resources=minimal" >> $GITHUB_OUTPUT + echo "debug=true" >> $GITHUB_OUTPUT + ;; + "staging") + echo "replicas=2" >> $GITHUB_OUTPUT + echo "resources=standard" >> $GITHUB_OUTPUT + echo "debug=false" >> $GITHUB_OUTPUT + ;; + "production") + echo "replicas=3" >> $GITHUB_OUTPUT + echo "resources=high" >> $GITHUB_OUTPUT + echo "debug=false" >> $GITHUB_OUTPUT + ;; + esac + + - name: 保存环境副本数配置 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'replicas-${{ matrix.environment }}' + state-value: ${{ steps.env-config.outputs.replicas }} + default-value: '1' + cache-prefix: 'env-config' + + - name: 保存环境资源配置 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'resources-${{ matrix.environment }}' + state-value: ${{ steps.env-config.outputs.resources }} + default-value: 'minimal' + cache-prefix: 'env-config' + + - name: 保存调试模式配置 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'debug-${{ matrix.environment }}' + state-value: ${{ steps.env-config.outputs.debug }} + default-value: 'true' + cache-prefix: 'env-config' + + - name: 检查环境健康状态 + id: health-check + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'health-status-${{ matrix.environment }}' + default-value: 'unknown' + action: 'get' + cache-prefix: 'health' + + - name: 初始化环境健康状态 + if: steps.health-check.outputs.state-value == 'unknown' + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'health-status-${{ matrix.environment }}' + state-value: 'healthy' + action: 'set' + cache-prefix: 'health' + + - name: 环境配置总结 + run: | + echo "🌍 环境 ${{ matrix.environment }} 配置:" + echo " - 副本数: ${{ steps.env-config.outputs.replicas }}" + echo " - 资源配置: ${{ steps.env-config.outputs.resources }}" + echo " - 调试模式: ${{ steps.env-config.outputs.debug }}" + echo " - 健康状态: ${{ steps.health-check.outputs.state-value }}" + + compare-environments: + needs: setup-environments + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 获取开发环境配置 + id: dev-config + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'replicas-dev' + action: 'get' + cache-prefix: 'env-config' + + - name: 获取预发布环境配置 + id: staging-config + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'replicas-staging' + action: 'get' + cache-prefix: 'env-config' + + - name: 获取生产环境配置 + id: prod-config + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'replicas-production' + action: 'get' + cache-prefix: 'env-config' + + - name: 环境配置对比 + run: | + echo "🔍 环境配置对比:" + echo " - 开发环境副本数: ${{ steps.dev-config.outputs.state-value }}" + echo " - 预发布环境副本数: ${{ steps.staging-config.outputs.state-value }}" + echo " - 生产环境副本数: ${{ steps.prod-config.outputs.state-value }}" + + # 检查配置一致性 + if [[ "${{ steps.dev-config.outputs.state-value }}" < "${{ steps.staging-config.outputs.state-value }}" ]] && \ + [[ "${{ steps.staging-config.outputs.state-value }}" < "${{ steps.prod-config.outputs.state-value }}" ]]; then + echo "✅ 环境配置递增合理" + else + echo "⚠️ 环境配置可能需要检查" + fi + + environment-health-monitor: + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 模拟健康检查 - 开发环境 + run: | + # 模拟健康检查逻辑 + HEALTH_STATUS="healthy" + if [[ $((RANDOM % 10)) -eq 0 ]]; then + HEALTH_STATUS="unhealthy" + fi + echo "dev_health=${HEALTH_STATUS}" >> $GITHUB_ENV + + - name: 更新开发环境健康状态 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'health-status-dev' + state-value: ${{ env.dev_health }} + action: 'set' + cache-prefix: 'health' + + - name: 模拟健康检查 - 预发布环境 + run: | + HEALTH_STATUS="healthy" + if [[ $((RANDOM % 15)) -eq 0 ]]; then + HEALTH_STATUS="degraded" + fi + echo "staging_health=${HEALTH_STATUS}" >> $GITHUB_ENV + + - name: 更新预发布环境健康状态 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'health-status-staging' + state-value: ${{ env.staging_health }} + action: 'set' + cache-prefix: 'health' + + - name: 模拟健康检查 - 生产环境 + run: | + HEALTH_STATUS="healthy" + if [[ $((RANDOM % 20)) -eq 0 ]]; then + HEALTH_STATUS="critical" + fi + echo "prod_health=${HEALTH_STATUS}" >> $GITHUB_ENV + + - name: 更新生产环境健康状态 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'health-status-production' + state-value: ${{ env.prod_health }} + action: 'set' + cache-prefix: 'health' + + generate-environment-report: + needs: [setup-environments, environment-health-monitor] + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 收集所有环境状态 + id: collect-status + run: | + echo "🔍 收集环境状态信息..." + + # 创建状态收集脚本 + cat > collect_status.sh << 'EOF' + #!/bin/bash + + ENVIRONMENTS=("dev" "staging" "production") + + echo "| 环境 | 副本数 | 资源配置 | 调试模式 | 健康状态 |" + echo "|------|--------|----------|----------|----------|" + + for env in "${ENVIRONMENTS[@]}"; do + echo "| $env | - | - | - | - |" + done + EOF + + chmod +x collect_status.sh + ./collect_status.sh + + - name: 获取开发环境完整状态 + id: dev-status + run: | + echo "=== 开发环境状态 ===" + + - name: 读取开发环境配置 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'replicas-dev' + action: 'get' + cache-prefix: 'env-config' + + - name: 读取开发环境健康状态 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'health-status-dev' + action: 'get' + cache-prefix: 'health' + + - name: 获取预发布环境完整状态 + run: | + echo "=== 预发布环境状态 ===" + + - name: 获取生产环境完整状态 + run: | + echo "=== 生产环境状态 ===" + + - name: 生成环境状态报告 + run: | + echo "📊 多环境状态报告 - $(date)" + echo "==================================" + echo "" + echo "本次工作流程已完成以下操作:" + echo "✅ 环境配置初始化和更新" + echo "✅ 健康状态监控" + echo "✅ 配置对比分析" + echo "" + echo "所有环境状态已缓存,可供后续工作流使用。" + + cleanup-old-states: + if: github.event.inputs.config_update == 'true' + runs-on: ubuntu-latest + steps: + - name: 检出代码 + uses: actions/checkout@v4 + + - name: 清理过期的配置状态 + run: | + echo "🧹 清理过期的配置状态..." + # 这里可以实现清理逻辑 + # 例如,设置空值来清理特定的缓存状态 + + - name: 重置所有环境的临时状态 + uses: .actions/xgj/cache-state@v1 + with: + state-key: 'temp-maintenance-mode' + state-value: '' + action: 'set' + cache-prefix: 'maintenance'