diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 2fd552b..b3872e3 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -36,25 +36,42 @@ jobs: - uses: r-lib/actions/setup-pandoc@d3c5be51b12e724e68f33216ca3c148b66d5f0b6 # v2 - - uses: r-lib/actions/setup-r@6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 # v2 + - uses: r-lib/actions/setup-r@d3c5be51b12e724e68f33216ca3c148b66d5f0b6 # v2 with: r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true - - uses: r-lib/actions/setup-r-dependencies@6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 # v2 + # Build stringfish before dependency setup because packages built during + # resolution can load it and fail on a prebuilt oneTBB ABI mismatch. + - name: Source-build stringfish before dependency resolution + run: >- + Rscript -e 'install.packages(c("RcppParallel", "stringfish"), type = "source", + repos = "https://cloud.r-project.org")' + + - uses: r-lib/actions/setup-r-dependencies@d3c5be51b12e724e68f33216ca3c148b66d5f0b6 # v2 with: + # Refresh the dependency cache after the macOS qs2/RcppParallel TBB ABI mismatch. + cache-version: '2' extra-packages: | any::rcmdcheck any::testthat needs: check + # Rebuild every installed package that links to RcppParallel against the + # same local oneTBB ABI before loading kaefa or its test dependencies. + - name: Rebuild RcppParallel-linked packages from source + run: >- + Rscript -e 'r <- "https://cloud.r-project.org"; install.packages("RcppParallel", type = "source", repos = r); + linked <- tools::dependsOnPkgs("RcppParallel", dependencies = "LinkingTo"); + if (length(linked)) install.packages(linked, type = "source", repos = r)' + - name: Run Zh formula regression tests run: | Rscript -e 'install.packages(".", repos = NULL, type = "source")' Rscript -e 'library(kaefa); testthat::test_file("tests/testthat/test-zh-misfit-decision-rule.R")' - - uses: r-lib/actions/check-r-package@6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 # v2 + - uses: r-lib/actions/check-r-package@d3c5be51b12e724e68f33216ca3c148b66d5f0b6 # v2 with: error-on: '"error"' upload-snapshots: true diff --git a/.github/workflows/test-fast.yaml b/.github/workflows/test-fast.yaml index ea1cf69..4032abe 100644 --- a/.github/workflows/test-fast.yaml +++ b/.github/workflows/test-fast.yaml @@ -16,15 +16,30 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: r-lib/actions/setup-r@6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 # v2 + - uses: r-lib/actions/setup-r@d3c5be51b12e724e68f33216ca3c148b66d5f0b6 # v2 with: use-public-rspm: true - - uses: r-lib/actions/setup-r-dependencies@6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 # v2 + # Build stringfish before dependency setup because packages built during + # resolution can load it and fail on a prebuilt oneTBB ABI mismatch. + - name: Source-build stringfish before dependency resolution + run: >- + Rscript -e 'install.packages(c("RcppParallel", "stringfish"), type = "source", + repos = "https://cloud.r-project.org")' + + - uses: r-lib/actions/setup-r-dependencies@d3c5be51b12e724e68f33216ca3c148b66d5f0b6 # v2 with: extra-packages: any::testthat needs: check + # Rebuild every installed package that links to RcppParallel against the + # same local oneTBB ABI before loading kaefa or its test dependencies. + - name: Rebuild RcppParallel-linked packages from source + run: >- + Rscript -e 'r <- "https://cloud.r-project.org"; install.packages("RcppParallel", type = "source", repos = r); + linked <- tools::dependsOnPkgs("RcppParallel", dependencies = "LinkingTo"); + if (length(linked)) install.packages(linked, type = "source", repos = r)' + - name: Install kaefa package for fast tests run: R CMD INSTALL . diff --git a/.github/workflows/test-suite.yaml b/.github/workflows/test-suite.yaml index 439b9b7..886de1b 100644 --- a/.github/workflows/test-suite.yaml +++ b/.github/workflows/test-suite.yaml @@ -32,15 +32,30 @@ jobs: steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - uses: r-lib/actions/setup-r@6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 # v2 + - uses: r-lib/actions/setup-r@d3c5be51b12e724e68f33216ca3c148b66d5f0b6 # v2 with: use-public-rspm: true - - uses: r-lib/actions/setup-r-dependencies@6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 # v2 + # Build stringfish before dependency setup because packages built during + # resolution can load it and fail on a prebuilt oneTBB ABI mismatch. + - name: Source-build stringfish before dependency resolution + run: >- + Rscript -e 'install.packages(c("RcppParallel", "stringfish"), type = "source", + repos = "https://cloud.r-project.org")' + + - uses: r-lib/actions/setup-r-dependencies@d3c5be51b12e724e68f33216ca3c148b66d5f0b6 # v2 with: extra-packages: any::testthat needs: check + # Rebuild every installed package that links to RcppParallel against the + # same local oneTBB ABI before loading kaefa or its test dependencies. + - name: Rebuild RcppParallel-linked packages from source + run: >- + Rscript -e 'r <- "https://cloud.r-project.org"; install.packages("RcppParallel", type = "source", repos = r); + linked <- tools::dependsOnPkgs("RcppParallel", dependencies = "LinkingTo"); + if (length(linked)) install.packages(linked, type = "source", repos = r)' + - name: Install kaefa package run: R CMD INSTALL . diff --git a/AGENTS.md b/AGENTS.md index e83e8e3..f93dc10 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -96,12 +96,12 @@ working on this repo. Keep this block; re-runs replace it in place. DOM-decomposes emails and files into a persisted knowledge graph). Each component is a standalone program that must ALSO work as a git submodule — grown separately and together. -- Sibling components: **waf-ids-ai-soc** (WAF/IDS/AI SOC/LB/APIM), +- Sibling components: **wardnet** (WAF/IDS/AI SOC/LB/APIM), **clearfolio** (document viewer), **pg-erd-cloud** (ERD tool), **contextual-orchestrator** (LLM cost/perf/upstream-LB gateway, beyond LiteLLM), **codec-carver** (STT/omni-modal speech-video codec), **fast-mlsirm** (LLM-as-a-Judge calibration + evaluation-item quality; uses - aFIPC FIPC + kaefa item-fit), **feelanet-adfs** (passwordless SSO — + aFIPC FIPC + kaefa item-fit), **keyverse** (passwordless SSO — OIDC/SCIM/ADFS/LDAP/FIDO2/OAuth2.1), **newsdom-api** (PDF->DOM sidecar), and **semantic-data-portal** (upper-ontology/catalog/governance plane with its own graph engine). diff --git a/docs/superpowers/plans/2026-07-02-kaefa-2b-krw-sale-readiness.md b/docs/superpowers/plans/2026-07-02-kaefa-2b-krw-sale-readiness.md index d5192fe..4be08d6 100644 --- a/docs/superpowers/plans/2026-07-02-kaefa-2b-krw-sale-readiness.md +++ b/docs/superpowers/plans/2026-07-02-kaefa-2b-krw-sale-readiness.md @@ -251,11 +251,11 @@ Guardrails: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: r-lib/actions/setup-r@6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 # v2 + - uses: r-lib/actions/setup-r@d3c5be51b12e724e68f33216ca3c148b66d5f0b6 # v2 with: use-public-rspm: true - - uses: r-lib/actions/setup-r-dependencies@6f6e5bc62fba3a704f74e7ad7ef7676c5c6a2590 # v2 + - uses: r-lib/actions/setup-r-dependencies@d3c5be51b12e724e68f33216ca3c148b66d5f0b6 # v2 with: extra-packages: any::testthat needs: check diff --git a/docs/traceability/r-lib-actions-v2.12.1.md b/docs/traceability/r-lib-actions-v2.12.1.md new file mode 100644 index 0000000..fa52bd6 --- /dev/null +++ b/docs/traceability/r-lib-actions-v2.12.1.md @@ -0,0 +1,29 @@ +# r-lib/actions v2.12.1 공급망 추적 + +## 결정 + +세 개의 R 검증 workflow에서 사용하는 setup-pandoc, setup-r, +setup-r-dependencies, check-r-package를 v2.12.1 release commit +d3c5be51b12e724e68f33216ca3c148b66d5f0b6으로 통일한다. 전체 commit SHA 외의 태그·브랜치·짧은 SHA는 +회귀 계약이 거부한다. + +## 호환성 범위 + +공식 NEWS에 따르면 v2.12는 Node.js 24 전환, public RSPM 기본값 조정, +아키텍처별 cache key와 Pandoc 3.8.3을 포함하고, v2.12.1은 setup-r URL parser +경고와 Quarto action을 갱신한다. 현재 workflow의 R matrix, 권한, testthat 실행, +--no-tests 분리와 scheduled full-suite 계약은 변경하지 않는다. + +## 되돌리기 + +runner 또는 package 호환성 회귀가 확인되면 네 action을 함께 마지막 검증 SHA로 +되돌리고 R-CMD-check, fast/full test suite와 중앙 보안 검사를 같은 헤드에서 다시 +수행한다. 일부 action만 되돌리거나 이동 태그로 우회하지 않는다. + +## 참고문헌 + +R-lib. (2026, June 23). *r-lib/actions v2.12.1* [Software release notes]. +https://github.com/r-lib/actions/blob/d3c5be51b12e724e68f33216ca3c148b66d5f0b6/NEWS.md + +R-lib. (2026). *Update NEWS for v2.12.1* [Source code commit]. +https://github.com/r-lib/actions/commit/d3c5be51b12e724e68f33216ca3c148b66d5f0b6 diff --git a/tests/testthat/test-ci-action-pins.R b/tests/testthat/test-ci-action-pins.R new file mode 100644 index 0000000..4203052 --- /dev/null +++ b/tests/testthat/test-ci-action-pins.R @@ -0,0 +1,142 @@ +testthat::test_that("CI uses exactly the reviewed r-lib action references", { + reviewed_sha <- "d3c5be51b12e724e68f33216ca3c148b66d5f0b6" + workflow_paths <- c( + testthat::test_path("..", "..", ".github", "workflows", "R-CMD-check.yaml"), + testthat::test_path("..", "..", ".github", "workflows", "test-fast.yaml"), + testthat::test_path("..", "..", ".github", "workflows", "test-suite.yaml") + ) + expected_actions <- list( + "R-CMD-check.yaml" = c( + "setup-pandoc", + "setup-r", + "setup-r-dependencies", + "check-r-package" + ), + "test-fast.yaml" = c("setup-r", "setup-r-dependencies"), + "test-suite.yaml" = c("setup-r", "setup-r-dependencies") + ) + action_pattern <- paste0( + "r-lib/actions/", + "(setup-pandoc|setup-r-dependencies|setup-r|check-r-package)", + "@[^[:space:]#]+" + ) + + for (workflow_path in workflow_paths) { + workflow_text <- paste(readLines(workflow_path, warn = FALSE), collapse = "\n") + action_refs <- regmatches( + workflow_text, + gregexpr(action_pattern, workflow_text, perl = TRUE) + )[[1]] + expected_refs <- paste0( + "r-lib/actions/", + expected_actions[[basename(workflow_path)]], + "@", + reviewed_sha + ) + + testthat::expect_identical( + action_refs, + expected_refs, + info = paste("Unexpected r-lib action set in", basename(workflow_path)) + ) + } +}) + +testthat::test_that("R CMD check refreshes the reviewed dependency cache ABI", { + workflow_path <- testthat::test_path( + "..", "..", ".github", "workflows", "R-CMD-check.yaml" + ) + workflow_lines <- readLines(workflow_path, warn = FALSE) + dependency_step <- grep( + "r-lib/actions/setup-r-dependencies@", + workflow_lines, + fixed = TRUE + ) + testthat::expect_length(dependency_step, 1L) + dependency_block <- workflow_lines[ + dependency_step:min(dependency_step + 8L, length(workflow_lines)) + ] + + active_cache_version_pattern <- paste0( + "^[[:space:]]*cache-version:[[:space:]]*", + "['\\\"]2['\\\"][[:space:]]*(#.*)?$" + ) + testthat::expect_false( + grepl(active_cache_version_pattern, "# cache-version: '2'", perl = TRUE), + info = "A commented cache-version example must not satisfy the contract" + ) + testthat::expect_true( + any(grepl(active_cache_version_pattern, dependency_block, perl = TRUE)), + info = "The reviewed macOS TBB ABI cache refresh must remain explicit" + ) +}) + +testthat::test_that("CI rebuilds oneTBB dependents around dependency setup", { + workflow_paths <- c( + testthat::test_path("..", "..", ".github", "workflows", "R-CMD-check.yaml"), + testthat::test_path("..", "..", ".github", "workflows", "test-fast.yaml"), + testthat::test_path("..", "..", ".github", "workflows", "test-suite.yaml") + ) + + for (workflow_path in workflow_paths) { + workflow_lines <- readLines(workflow_path, warn = FALSE) + pre_step <- grep( + "name: Source-build stringfish before dependency resolution", + workflow_lines, + fixed = TRUE + ) + dependency_step <- grep( + "r-lib/actions/setup-r-dependencies@", + workflow_lines, + fixed = TRUE + ) + post_step <- grep( + "name: Rebuild RcppParallel-linked packages from source", + workflow_lines, + fixed = TRUE + ) + + testthat::expect_length(pre_step, 1L) + testthat::expect_length(dependency_step, 1L) + testthat::expect_length(post_step, 1L) + testthat::expect_true(pre_step < dependency_step) + testthat::expect_true(dependency_step < post_step) + + pre_block <- workflow_lines[ + pre_step:min(pre_step + 5L, length(workflow_lines)) + ] + post_block <- workflow_lines[ + post_step:min(post_step + 7L, length(workflow_lines)) + ] + testthat::expect_true(any(grepl( + 'install.packages(c("RcppParallel", "stringfish")', + pre_block, + fixed = TRUE + ))) + testthat::expect_true(any(grepl( + 'repos = "https://cloud.r-project.org"', + pre_block, + fixed = TRUE + ))) + testthat::expect_true(any(grepl( + 'type = "source"', + pre_block, + fixed = TRUE + ))) + testthat::expect_true(any(grepl( + 'dependsOnPkgs("RcppParallel", dependencies = "LinkingTo")', + post_block, + fixed = TRUE + ))) + testthat::expect_true(any(grepl( + 'install.packages("RcppParallel", type = "source"', + post_block, + fixed = TRUE + ))) + testthat::expect_true(any(grepl( + 'install.packages(linked, type = "source"', + post_block, + fixed = TRUE + ))) + } +})