feat: add production capability foundations

This commit is contained in:
donghyeon-ka
2026-07-31 23:50:44 +09:00
parent b3add0162d
commit 567422f2e5
757 changed files with 132385 additions and 2146 deletions
+7
View File
@@ -101,6 +101,13 @@ gates:
workflow: ci-quality-gates.yml
job: gate-matrix-lint
execution: job
- id: redis-standalone
release_blocking: true
mechanism: workflow-job
ref: redis-standalone
workflow: ci-quality-gates.yml
job: redis-standalone
execution: job
- id: quality-release-gate
release_blocking: true
mechanism: workflow-job
+1 -1
View File
@@ -5,7 +5,7 @@ readonly SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
readonly REPO_ROOT="$(git -C "${SCRIPT_DIR}" rev-parse --show-toplevel)"
readonly EXPECTED_SCRIPT_DIR="$(cd -- "${REPO_ROOT}/.github/scripts" && pwd -P)"
readonly MATRIX="${REPO_ROOT}/.github/ci-gate-matrix.yml"
readonly EXPECTED_GATE_COUNT=19
readonly EXPECTED_GATE_COUNT=20
if [[ "${SCRIPT_DIR}" != "${EXPECTED_SCRIPT_DIR}" ]]; then
printf '::error::gate-matrix-lint: script resolved outside the repository .github/scripts directory\n' >&2
+30 -1
View File
@@ -70,6 +70,33 @@ jobs:
- name: Verify the gate matrix against the repository
run: bash .github/scripts/verify-gate-matrix.sh
redis-standalone:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
with:
distribution: temurin
java-version: "21.0.11+10"
cache: gradle
cache-dependency-path: |
src/**/*.gradle
src/**/gradle-wrapper.properties
src/**/gradle.lockfile
- name: Verify standalone Redis policy, provider, and composition contracts
working-directory: src
run: >-
./gradlew
:application-core:redisPolicyContractTest
:shared-contract:edgeRateLimitContractTest
:adapter:outbound:cache-redis:check
:app-bootstrap:redisCompositionTest
verifyCleanArchitectureDependencies
verifyEnvKeys
verifyPublicPathSnapshot
verifyConfigurationPropertiesProcessor
--no-daemon --stacktrace
# Advisory only. Quarantine expiry/drift remains blocking through verifyQuarantineSunset in check.
quarantine:
runs-on: ubuntu-latest
@@ -94,6 +121,7 @@ jobs:
- quality-gates
- sample-off
- gate-matrix-lint
- redis-standalone
if: always()
runs-on: ubuntu-latest
steps:
@@ -102,9 +130,10 @@ jobs:
QUALITY_RESULT: ${{ needs.quality-gates.result }}
SAMPLE_OFF_RESULT: ${{ needs.sample-off.result }}
MATRIX_RESULT: ${{ needs.gate-matrix-lint.result }}
REDIS_RESULT: ${{ needs.redis-standalone.result }}
run: |
set -euo pipefail
for result in "${QUALITY_RESULT}" "${SAMPLE_OFF_RESULT}" "${MATRIX_RESULT}"; do
for result in "${QUALITY_RESULT}" "${SAMPLE_OFF_RESULT}" "${MATRIX_RESULT}" "${REDIS_RESULT}"; do
if [[ "${result}" != "success" ]]; then
echo "::error::release-gate: required job result was ${result}"
exit 1
@@ -0,0 +1,375 @@
name: redis-production-readiness
on:
schedule:
- cron: "23 18 * * *"
workflow_dispatch:
push:
tags:
- "v*-rc.*"
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false
jobs:
resolve-redis-readiness:
runs-on: ubuntu-latest
outputs:
selected: ${{ steps.resolve.outputs.selected }}
candidates: ${{ steps.resolve.outputs.candidates }}
selected_count: ${{ steps.resolve.outputs.selected_count }}
sentinel_required: ${{ steps.resolve.outputs.sentinel_required }}
cluster_required: ${{ steps.resolve.outputs.cluster_required }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
with:
distribution: temurin
java-version: "21.0.11+10"
cache: gradle
- name: Generate the strict checked-in readiness control artifact
working-directory: src
run: ./gradlew writeRedisCiMatrix --no-daemon --stacktrace
- id: resolve
name: Transport the generated matrix to job outputs
shell: python
run: |
import json
import os
from pathlib import Path
matrix_path = Path(
"src/build/redis-evidence/control/redis-readiness-matrix.json"
)
matrix = json.loads(matrix_path.read_text(encoding="utf-8"))
if matrix["releaseQualification"] != "NOT_CLAIMED":
raise SystemExit("resolver control artifact must not claim release qualification")
with Path(os.environ["GITHUB_OUTPUT"]).open("a", encoding="utf-8") as stream:
stream.write(
"selected="
+ json.dumps(matrix["selected"], separators=(",", ":"))
+ "\n"
)
stream.write(
"candidates="
+ json.dumps(
matrix["implementedCandidates"], separators=(",", ":")
)
+ "\n"
)
stream.write(f"selected_count={matrix['selectedCount']}\n")
stream.write(
"sentinel_required="
+ str(matrix["topologyJobs"]["sentinel"]).lower()
+ "\n"
)
stream.write(
"cluster_required="
+ str(matrix["topologyJobs"]["cluster"]).lower()
+ "\n"
)
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4
with:
name: redis-readiness-control
path: src/build/redis-evidence/control
if-no-files-found: error
retention-days: 30
redis-security:
needs: resolve-redis-readiness
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
with:
distribution: temurin
java-version: "21.0.11+10"
cache: gradle
- id: redis-tests
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:redisSecurityTest --no-daemon --stacktrace
- id: redis-evidence-sanitizer
if: always()
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:verifyRedisEvidenceArtifactsForUpload --no-daemon --stacktrace
- if: ${{ always() && steps.redis-evidence-sanitizer.outcome == 'success' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4
with:
name: redis-security-evidence
path: src/adapter/outbound/cache-redis/build/redis-evidence
if-no-files-found: error
retention-days: 14
redis-sentinel:
needs: resolve-redis-readiness
if: ${{ needs.resolve-redis-readiness.outputs.sentinel_required == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
with:
distribution: temurin
java-version: "21.0.11+10"
cache: gradle
- id: redis-tests
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:redisSentinelTest --no-daemon --stacktrace
- id: redis-evidence-sanitizer
if: always()
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:verifyRedisEvidenceArtifactsForUpload --no-daemon --stacktrace
- if: ${{ always() && steps.redis-evidence-sanitizer.outcome == 'success' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4
with:
name: redis-sentinel-evidence
path: src/adapter/outbound/cache-redis/build/redis-evidence
if-no-files-found: error
retention-days: 14
redis-cluster:
needs: resolve-redis-readiness
if: ${{ needs.resolve-redis-readiness.outputs.cluster_required == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
with:
distribution: temurin
java-version: "21.0.11+10"
cache: gradle
- id: redis-tests
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:redisClusterTest --no-daemon --stacktrace
- id: redis-evidence-sanitizer
if: always()
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:verifyRedisEvidenceArtifactsForUpload --no-daemon --stacktrace
- if: ${{ always() && steps.redis-evidence-sanitizer.outcome == 'success' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4
with:
name: redis-cluster-evidence
path: src/adapter/outbound/cache-redis/build/redis-evidence
if-no-files-found: error
retention-days: 14
redis-fault:
needs: resolve-redis-readiness
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
with:
distribution: temurin
java-version: "21.0.11+10"
cache: gradle
- id: redis-tests
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:redisFaultTest --no-daemon --stacktrace
- id: redis-evidence-sanitizer
if: always()
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:verifyRedisEvidenceArtifactsForUpload --no-daemon --stacktrace
- if: ${{ always() && steps.redis-evidence-sanitizer.outcome == 'success' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4
with:
name: redis-fault-evidence
path: src/adapter/outbound/cache-redis/build/redis-evidence
if-no-files-found: error
retention-days: 14
redis-compatibility:
needs: resolve-redis-readiness
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
with:
distribution: temurin
java-version: "21.0.11+10"
cache: gradle
- id: redis-tests
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:redisCompatibilityTest --no-daemon --stacktrace
- id: redis-evidence-sanitizer
if: always()
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:verifyRedisEvidenceArtifactsForUpload --no-daemon --stacktrace
- if: ${{ always() && steps.redis-evidence-sanitizer.outcome == 'success' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4
with:
name: redis-compatibility-evidence
path: src/adapter/outbound/cache-redis/build/redis-evidence
if-no-files-found: error
retention-days: 14
selected-card-readiness:
needs: resolve-redis-readiness
if: ${{ needs.resolve-redis-readiness.outputs.selected_count != '0' }}
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.resolve-redis-readiness.outputs.selected) }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
with:
distribution: temurin
java-version: "21.0.11+10"
cache: gradle
- id: redis-tests
working-directory: src
run: ./gradlew ${{ matrix.readinessTask }} --no-daemon --stacktrace
- id: redis-evidence-sanitizer
if: always()
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:verifyRedisEvidenceArtifactsForUpload --no-daemon --stacktrace
- if: ${{ always() && steps.redis-evidence-sanitizer.outcome == 'success' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4
with:
name: redis-selected-${{ matrix.cardId }}
path: src/adapter/outbound/cache-redis/build/redis-evidence
if-no-files-found: error
retention-days: 30
redis-all-candidates:
needs: resolve-redis-readiness
if: ${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
with:
distribution: temurin
java-version: "21.0.11+10"
cache: gradle
- id: redis-tests
working-directory: src
run: ./gradlew redisAllImplementedCandidates --no-daemon --stacktrace
- id: redis-evidence-sanitizer
if: always()
working-directory: src
run: ./gradlew :adapter:outbound:cache-redis:verifyRedisEvidenceArtifactsForUpload --no-daemon --stacktrace
- if: ${{ always() && steps.redis-evidence-sanitizer.outcome == 'success' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # actions/upload-artifact@v4
with:
name: redis-all-candidates-evidence
path: src/adapter/outbound/cache-redis/build/redis-evidence
if-no-files-found: error
retention-days: 14
redis-production-readiness:
needs:
- resolve-redis-readiness
- selected-card-readiness
if: ${{ always() && needs.resolve-redis-readiness.result == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Require the exact selected matrix result
shell: python
env:
SELECTED_COUNT: ${{ needs.resolve-redis-readiness.outputs.selected_count }}
SELECTED_JOB_RESULT: ${{ needs.selected-card-readiness.result }}
run: |
import os
selected_count_text = os.environ["SELECTED_COUNT"]
selected_job_result = os.environ["SELECTED_JOB_RESULT"]
if not selected_count_text.isdecimal():
raise SystemExit("selected_count must be a non-negative integer")
selected_count = int(selected_count_text)
expected_result = "skipped" if selected_count == 0 else "success"
if selected_job_result != expected_result:
raise SystemExit(
"selected-card-readiness result mismatch: "
f"count={selected_count}, expected={expected_result}, "
f"actual={selected_job_result}"
)
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # actions/checkout@v4.2.2
- uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # actions/setup-java@v4.7.1
with:
distribution: temurin
java-version: "21.0.11+10"
cache: gradle
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # actions/download-artifact@v4.3.0
with:
name: redis-readiness-control
path: ${{ runner.temp }}/redis-readiness/control
- if: >-
${{
needs.resolve-redis-readiness.outputs.selected_count != '0'
&& needs.selected-card-readiness.result == 'success'
}}
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # actions/download-artifact@v4.3.0
with:
pattern: redis-selected-*
path: ${{ runner.temp }}/redis-readiness/selected
- name: Record the downloaded selected artifact inventory
shell: python
env:
GITHUB_RUN_ID: ${{ github.run_id }}
SELECTED_JSON: ${{ needs.resolve-redis-readiness.outputs.selected }}
SELECTED_COUNT: ${{ needs.resolve-redis-readiness.outputs.selected_count }}
SELECTED_JOB_RESULT: ${{ needs.selected-card-readiness.result }}
REDIS_SELECTED_DIRECTORY: ${{ runner.temp }}/redis-readiness/selected
REDIS_CI_RESULT_FILE: ${{ runner.temp }}/redis-readiness/redis-ci-result.json
run: |
import json
import os
from pathlib import Path
selected = json.loads(os.environ["SELECTED_JSON"])
if not isinstance(selected, list):
raise SystemExit("selected matrix must be a JSON array")
expected_names = sorted(
"redis-selected-" + entry["cardId"] for entry in selected
)
if len(expected_names) != int(os.environ["SELECTED_COUNT"]):
raise SystemExit("selected_count does not match the selected matrix")
if len(expected_names) != len(set(expected_names)):
raise SystemExit("selected matrix contains duplicate artifact names")
selected_directory = Path(os.environ["REDIS_SELECTED_DIRECTORY"])
actual_names = (
sorted(path.name for path in selected_directory.iterdir() if path.is_dir())
if selected_directory.is_dir()
else []
)
if actual_names != expected_names:
raise SystemExit(
"downloaded selected artifact inventory mismatch: "
f"expected={expected_names}, actual={actual_names}"
)
result = {
"schemaVersion": 1,
"runId": os.environ["GITHUB_RUN_ID"],
"selectedCount": len(expected_names),
"selectedJobResult": os.environ["SELECTED_JOB_RESULT"],
"selectedArtifactNames": actual_names,
}
result_path = Path(os.environ["REDIS_CI_RESULT_FILE"])
result_path.parent.mkdir(parents=True, exist_ok=True)
result_path.write_text(
json.dumps(result, sort_keys=True, separators=(",", ":")) + "\n",
encoding="utf-8",
)
- if: ${{ needs.resolve-redis-readiness.outputs.selected_count == '0' }}
working-directory: src
run: >-
./gradlew verifyRedisSelectedEvidenceArtifacts redisProductionReadiness
-PredisControlDirectory=${{ runner.temp }}/redis-readiness/control
-PredisCiResultFile=${{ runner.temp }}/redis-readiness/redis-ci-result.json
--no-daemon --stacktrace
- if: ${{ needs.resolve-redis-readiness.outputs.selected_count != '0' }}
working-directory: src
run: >-
./gradlew verifyRedisSelectedEvidenceArtifacts redisProductionReadiness
-PredisControlDirectory=${{ runner.temp }}/redis-readiness/control
-PredisEvidenceDirectory=${{ runner.temp }}/redis-readiness/selected
-PredisCiResultFile=${{ runner.temp }}/redis-readiness/redis-ci-result.json
--no-daemon --stacktrace