From da8ed65dd1cdb9911f81771c19c6c00521f166a1 Mon Sep 17 00:00:00 2001 From: Sin-Kang Date: Sun, 30 Aug 2026 21:10:14 +0900 Subject: [PATCH 1/2] feat: apply DevsLab OSS identity to devslab-kit --- .github/assets/readme-header.png | Bin 0 -> 12201 bytes .github/assets/social-preview.png | Bin 0 -> 17633 bytes README.ko.md | 6 +++ README.md | 6 +++ docs/assets/favicon.svg | 1 + docs/assets/logo.svg | 4 +- docs/assets/social-preview.png | Bin 0 -> 17633 bytes docs/index.ko.md | 5 +++ docs/index.md | 5 +++ docs/overrides/main.html | 10 +++++ docs/stylesheets/extra.css | 69 ++++++++++++++++++++++++++++++ mkdocs.yml | 3 +- scripts/check-brand-assets.mjs | 58 +++++++++++++++++++++++++ 13 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 .github/assets/readme-header.png create mode 100644 .github/assets/social-preview.png create mode 100644 docs/assets/favicon.svg create mode 100644 docs/assets/social-preview.png create mode 100644 docs/overrides/main.html create mode 100644 scripts/check-brand-assets.mjs diff --git a/.github/assets/readme-header.png b/.github/assets/readme-header.png new file mode 100644 index 0000000000000000000000000000000000000000..739ee1cb32128a536f2f31b5e4d3c1cd59f81841 GIT binary patch literal 12201 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 literal 0 HcmV?d00001 diff --git a/.github/assets/social-preview.png b/.github/assets/social-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..6c88ec30157047eab3e2f7516c10c89b014927dc GIT binary patch literal 17633 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 + devslab-kit - DevsLab 오픈소스 +

+ +**[DevsLab](https://devslab.kr/) 오픈소스** · [OSS 브랜드 가이드](https://devslab.kr/brand/open-source/) · 레지스트리 O10 + [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/devslab-kit-spring-boot-starter?logo=apachemaven)](https://central.sonatype.com/artifact/kr.devslab/devslab-kit-spring-boot-starter) [![Build](https://github.com/devslab-kr/devslab-kit/actions/workflows/build.yml/badge.svg)](https://github.com/devslab-kr/devslab-kit/actions/workflows/build.yml) [![codecov](https://codecov.io/gh/devslab-kr/devslab-kit/branch/main/graph/badge.svg)](https://codecov.io/gh/devslab-kr/devslab-kit) diff --git a/README.md b/README.md index cfa59ef..97f68f7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # devslab-kit +

+ devslab-kit - Open source by DevsLab +

+ +**Open source by [DevsLab](https://devslab.kr/)** · [OSS brand guide](https://devslab.kr/brand/open-source/) · Registry O10 + [![Maven Central](https://img.shields.io/maven-central/v/kr.devslab/devslab-kit-spring-boot-starter?logo=apachemaven)](https://central.sonatype.com/artifact/kr.devslab/devslab-kit-spring-boot-starter) [![Build](https://github.com/devslab-kr/devslab-kit/actions/workflows/build.yml/badge.svg)](https://github.com/devslab-kr/devslab-kit/actions/workflows/build.yml) [![codecov](https://codecov.io/gh/devslab-kr/devslab-kit/branch/main/graph/badge.svg)](https://codecov.io/gh/devslab-kr/devslab-kit) diff --git a/docs/assets/favicon.svg b/docs/assets/favicon.svg new file mode 100644 index 0000000..d725919 --- /dev/null +++ b/docs/assets/favicon.svg @@ -0,0 +1 @@ +devslab-kit \ No newline at end of file diff --git a/docs/assets/logo.svg b/docs/assets/logo.svg index 188ac30..d725919 100644 --- a/docs/assets/logo.svg +++ b/docs/assets/logo.svg @@ -1,3 +1 @@ - - - +devslab-kit \ No newline at end of file diff --git a/docs/assets/social-preview.png b/docs/assets/social-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..6c88ec30157047eab3e2f7516c10c89b014927dc GIT binary patch literal 17633 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 + + devslab-kitDevsLab 오픈소스 + + 재사용 가능한 **Spring Boot 4 플랫폼 스타터**. 애플리케이션에 끼워 넣으면 인증, 인가, 멀티테넌시, 동적 메뉴, 감사 로깅이 자동 구성으로 제공되고, 관리자 REST API와 바로 쓰는 관리자 콘솔까지 딸려옵니다. 매번 플랫폼 계층을 다시 만드는 대신 각 제품은 자기 도메인에만 diff --git a/docs/index.md b/docs/index.md index 0f79d98..e1e6c61 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,5 +1,10 @@ # devslab-kit + + + devslab-kitOpen source by DevsLab + + A reusable **Spring Boot 4 platform starter**. Drop it into an application and get authentication, authorization, multi-tenancy, dynamic menus and audit logging from auto-configuration — plus an admin REST API and a ready-made admin console — so each diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 0000000..f945336 --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} + +{% block site_meta %} + {{ super() }} + + + + + +{% endblock %} diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css index feb3dee..80b4e98 100644 --- a/docs/stylesheets/extra.css +++ b/docs/stylesheets/extra.css @@ -241,3 +241,72 @@ html[lang="ko"] .md-header [aria-label="Select language"]::before, html[lang="ko"] .md-header [aria-label*="언어"]::before { content: "한"; } + +/* === O10 project identity =============================================== */ +.oss-project-intro { + position: relative; + isolation: isolate; + display: flex; + align-items: center; + gap: 0.75rem; + width: fit-content; + max-width: 100%; + margin: 0.25rem 0 1.5rem; + padding: 0.75rem 1rem; + color: inherit; + border: 1px solid color-mix(in srgb, var(--md-accent-fg-color) 32%, transparent); + border-radius: 0.35rem; + text-decoration: none; +} + +.oss-project-intro[data-atmosphere="project"]::before { + position: absolute; + z-index: -1; + inset: 0; + content: ""; + background: linear-gradient(135deg, rgb(14 116 144 / 0.10), transparent 66%); + pointer-events: none; +} + +[data-md-color-scheme="slate"] .oss-project-intro[data-atmosphere="project"]::before { + background: linear-gradient(135deg, rgb(34 211 238 / 0.10), transparent 66%); +} + +.oss-project-intro img { + flex: 0 0 auto; + width: 2rem; + height: 2rem; +} + +.oss-project-intro span { + display: grid; + min-width: 0; +} + +.oss-project-intro strong, +.oss-project-intro small { + overflow-wrap: anywhere; +} + +.oss-project-intro small { + color: var(--md-default-fg-color--light); + font-size: 0.78rem; +} + +.oss-project-intro:hover, +.oss-project-intro:focus-visible { + border-color: var(--md-accent-fg-color); + text-decoration: none; +} + +@media (max-width: 28rem) { + .oss-project-intro { + align-items: flex-start; + } +} + +@media (forced-colors: active), print { + .oss-project-intro[data-atmosphere="project"]::before { + display: none; + } +} diff --git a/mkdocs.yml b/mkdocs.yml index 356bd42..3e91533 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -14,9 +14,10 @@ docs_dir: docs theme: name: material + custom_dir: docs/overrides language: en logo: assets/logo.svg - favicon: assets/logo.svg + favicon: assets/favicon.svg features: - navigation.tabs - navigation.sections diff --git a/scripts/check-brand-assets.mjs b/scripts/check-brand-assets.mjs new file mode 100644 index 0000000..dedc4ee --- /dev/null +++ b/scripts/check-brand-assets.mjs @@ -0,0 +1,58 @@ +import { createHash } from 'node:crypto'; +import { existsSync, readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +const root = resolve(import.meta.dirname, '..'); +const expectedHashes = new Map([ + ['.github/assets/readme-header.png', '181bb155b383789b0e88454a4ff9d16dafd22d5083140fa83255bb4e306785a2'], + ['.github/assets/social-preview.png', '4666515df130b8d85a5e4ac3fa38c5699af938c9a30eeab98a3ace0d5d301e2c'], + ['docs/assets/logo.svg', '927af7009de24040ac94c0586f3d93dac30c28b08c1d29b2a83c1bf74f1c4248'], + ['docs/assets/favicon.svg', '927af7009de24040ac94c0586f3d93dac30c28b08c1d29b2a83c1bf74f1c4248'], + ['docs/assets/social-preview.png', '4666515df130b8d85a5e4ac3fa38c5699af938c9a30eeab98a3ace0d5d301e2c'], +]); + +const read = (file) => readFileSync(resolve(root, file), 'utf8'); +const assert = (condition, message) => { + if (!condition) throw new Error(message); +}; + +for (const [file, expected] of expectedHashes) { + const absolute = resolve(root, file); + assert(existsSync(absolute), `missing required O10 source asset: ${file}`); + const actual = createHash('sha256').update(readFileSync(absolute)).digest('hex'); + assert(actual === expected, `${file} has ${actual}; expected the oss-brand v0.1.1 hash ${expected}`); +} + +const logo = read('docs/assets/logo.svg'); +assert(logo.includes('data-oss-project="O10"'), 'docs logo must identify the O10 project asset'); +assert(logo.includes('M5 5H13V13H5Z') && logo.includes('M13 10H19V22H13Z'), 'docs logo must use the O10 lattice geometry'); +assert(!logo.includes('M 4 4 L 28 4'), 'docs logo must not retain the former shared backend mark'); +assert(!logo.includes('M7 6H21V20H7Z'), 'docs logo must not use the O08 geometry'); +assert(!logo.includes('M5 8H13V14H5Z'), 'docs logo must not use the O09 geometry'); + +for (const [file, endorsement] of [ + ['README.md', 'Open source by [DevsLab](https://devslab.kr/)'], + ['README.ko.md', '[DevsLab](https://devslab.kr/) 오픈소스'], + ['docs/index.md', 'Open source by DevsLab'], + ['docs/index.ko.md', 'DevsLab 오픈소스'], +]) { + const content = read(file); + assert(content.includes('https://devslab.kr/brand/open-source/'), `${file} must link to the canonical OSS brand guide`); + assert(content.includes(endorsement), `${file} must include its localized DevsLab endorsement`); +} + +const mkdocs = read('mkdocs.yml'); +assert(mkdocs.includes('custom_dir: docs/overrides'), 'mkdocs must load the source-only template override'); +assert(mkdocs.includes('favicon: assets/favicon.svg'), 'mkdocs must use the O10 favicon source'); +assert(mkdocs.includes('logo: assets/logo.svg'), 'mkdocs must use the O10 logo source'); + +const metadata = read('docs/overrides/main.html'); +assert(metadata.includes('https://devslab-kit.devslab.kr/assets/social-preview.png'), 'metadata must use the local O10 social preview'); +assert(metadata.includes('og:image:alt') && metadata.includes('twitter:image:alt'), 'metadata must describe the social preview'); + +const styles = read('docs/stylesheets/extra.css'); +assert(styles.includes('.oss-project-intro') && styles.includes('data-atmosphere="project"'), 'docs need the O10 identity panel and scoped atmosphere'); +assert(styles.includes('pointer-events: none'), 'atmosphere decoration must not intercept input'); +assert(styles.includes('@media (forced-colors: active), print'), 'atmosphere must be suppressed for forced colors and print'); + +console.log(`O10 brand source contract passed (${expectedHashes.size} exact assets).`); From a9609e7e1b9f6b64915860af82f5ee5b6a9e1dfe Mon Sep 17 00:00:00 2001 From: Sin-Kang Date: Sun, 30 Aug 2026 21:26:07 +0900 Subject: [PATCH 2/2] fix: gate devslab-kit OSS brand verification --- .github/workflows/build.yml | 8 ++++++++ .github/workflows/release.yml | 8 ++++++++ scripts/check-brand-assets.mjs | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 233e852..01fc6e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,6 +21,14 @@ jobs: steps: - uses: actions/checkout@v4 + - name: Set up Node 22 + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Verify OSS brand sources + run: node scripts/check-brand-assets.mjs + - name: Set up Java 21 (GraalVM CE) uses: graalvm/setup-graalvm@v1 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 771fd37..7453772 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,6 +28,14 @@ jobs: - name: Checkout uses: actions/checkout@v6 + - name: Set up Node 22 + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Verify OSS brand sources + run: node scripts/check-brand-assets.mjs + # The build's Java toolchain pins vendor = GraalVM (build.gradle.kts), so the # runner must provide a GraalVM JDK 21 — a temurin JDK makes Gradle fail with # "Cannot find a Java installation … matching {vendor=GraalVM Community}". diff --git a/scripts/check-brand-assets.mjs b/scripts/check-brand-assets.mjs index dedc4ee..78811f9 100644 --- a/scripts/check-brand-assets.mjs +++ b/scripts/check-brand-assets.mjs @@ -52,6 +52,10 @@ assert(metadata.includes('og:image:alt') && metadata.includes('twitter:image:alt const styles = read('docs/stylesheets/extra.css'); assert(styles.includes('.oss-project-intro') && styles.includes('data-atmosphere="project"'), 'docs need the O10 identity panel and scoped atmosphere'); +const slateAtmosphere = `[data-md-color-scheme="slate"] .oss-project-intro[data-atmosphere="project"]::before { + background: linear-gradient(135deg, rgb(34 211 238 / 0.10), transparent 66%); +}`; +assert(styles.includes(slateAtmosphere), 'Material slate must apply the O10 cyan atmosphere directly at the 0.10 opacity cap'); assert(styles.includes('pointer-events: none'), 'atmosphere decoration must not intercept input'); assert(styles.includes('@media (forced-colors: active), print'), 'atmosphere must be suppressed for forced colors and print');