1 Commits

Author SHA1 Message Date
gitea-actions ad5c811a88 docs(readme): refresh contributors wall 2026-09-30 01:01:01 +00:00
16 changed files with 1390 additions and 36 deletions
+87
View File
@@ -0,0 +1,87 @@
name: ci
on:
pull_request:
# Release validation is owned by the release workflows rather than this CI
# workflow: `release-stable` has a verify job before publishing, and
# `release-beta` builds from its selected release commit. Keep this trigger
# focused on PRs, main, and manual reruns instead of duplicating tag/release
# events that would run after those release workflows have already selected
# or validated their commit.
push:
branches:
- main
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.event.pull_request.number || github.ref }}
# Prefer current-head signal over preserving superseded logs: PR authors often
# push fixups while this workflow is still running, and stale runs can report
# failures for commits reviewers no longer need to evaluate. Release workflows
# use cancel-in-progress: false where preserving build evidence matters more.
cancel-in-progress: true
jobs:
validate:
name: Validate workspace
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
# `scripts/postinstall.mjs` only prebuilds package/tool entrypoints that
# are needed immediately after install for linked bins and shared
# sidecar/platform imports. It intentionally skips app outputs because
# building all apps would make every install run a Next/Electron-adjacent
# app build, even when a developer only needs packages/tools.
#
# Fresh CI typecheck/test still need these specific generated declarations:
# - `apps/daemon/dist/*.d.ts` for e2e runtime-adapter tests that import
# daemon runtime modules
# - `apps/desktop/dist/main/index.d.ts` for `apps/packaged` imports of
# `@open-design/desktop/main`
# - `apps/web/dist/sidecar/index.d.ts` for `apps/packaged` imports of
# `@open-design/web/sidecar`
# If postinstall grows a targeted app type-generation phase covering these
# three exports without broad app builds, this CI prebuild can be removed.
- name: Prebuild workspace type declarations
run: |
pnpm --filter @open-design/daemon build
pnpm --filter @open-design/desktop build
pnpm --filter @open-design/web build:sidecar
- name: Typecheck workspaces
run: pnpm -r --workspace-concurrency=1 --if-present run typecheck
- name: Check residual JS in TypeScript packages
run: pnpm check:residual-js
- name: Test
run: pnpm test
# Keep workspace builds serialized so generated dist output and local
# runtime artifacts are produced in a deterministic order. Parallel
# recursive builds would surface late-package failures sooner, but the
# current workspace is small enough that safer logs and fewer shared-FS
# races outweigh the lost parallelism; revisit if the package count grows.
- name: Build workspaces
run: pnpm -r --workspace-concurrency=1 --if-present run build
+93
View File
@@ -0,0 +1,93 @@
name: landing-page-ci
on:
pull_request:
paths:
- .github/workflows/landing-page-ci.yml
- .github/workflows/landing-page.yml
- apps/landing-page/**
- package.json
- pnpm-lock.yaml
- pnpm-workspace.yaml
push:
branches:
- main
paths:
- .github/workflows/landing-page-ci.yml
- .github/workflows/landing-page.yml
- apps/landing-page/**
- package.json
- pnpm-lock.yaml
- pnpm-workspace.yaml
workflow_dispatch:
permissions:
contents: read
concurrency:
group: landing-page-ci-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
validate:
name: Validate landing page
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck landing page
run: pnpm --filter @open-design/landing-page typecheck
- name: Build landing page
run: pnpm --filter @open-design/landing-page build
- name: Verify zero external JavaScript
run: |
node <<'NODE'
const { readFileSync } = require('node:fs');
const html = readFileSync('apps/landing-page/out/index.html', 'utf8');
const forbidden = [
/<script\b[^>]*\bsrc=/i,
/type=["']module["']/i,
/\/_astro\/[^"'<>\s]+\.js/i,
];
for (const pattern of forbidden) {
if (pattern.test(html)) {
console.error(`Unexpected client JavaScript matched ${pattern}`);
process.exit(1);
}
}
NODE
- name: Verify Cloudflare image resizing URLs
run: |
node <<'NODE'
const { readFileSync } = require('node:fs');
const html = readFileSync('apps/landing-page/out/index.html', 'utf8');
const resizedUrls = html.match(/https:\/\/static\.open-design\.ai\/cdn-cgi\/image\//g) ?? [];
if (resizedUrls.length < 16) {
console.error(`Expected at least 16 Cloudflare resized image URLs, found ${resizedUrls.length}`);
process.exit(1);
}
if (/(?:src|content)=["']\/assets\/[A-Za-z0-9_.-]+\.png/.test(html)) {
console.error('Found local /assets/*.png image reference in generated landing HTML.');
process.exit(1);
}
NODE
+98
View File
@@ -0,0 +1,98 @@
name: landing-page-deploy
on:
push:
branches:
- main
paths:
- .github/workflows/landing-page-deploy.yml
- .github/workflows/landing-page-ci.yml
- apps/landing-page/**
- package.json
- pnpm-lock.yaml
- pnpm-workspace.yaml
workflow_dispatch:
permissions:
contents: read
deployments: write
concurrency:
group: landing-page-deploy-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
name: Deploy landing page
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
cache: pnpm
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Typecheck landing page
run: pnpm --filter @open-design/landing-page typecheck
- name: Build landing page
run: pnpm --filter @open-design/landing-page build
- name: Verify zero external JavaScript
run: |
node <<'NODE'
const { readFileSync } = require('node:fs');
const html = readFileSync('apps/landing-page/out/index.html', 'utf8');
const forbidden = [
/<script\b[^>]*\bsrc=/i,
/type=["']module["']/i,
/\/_astro\/[^"'<>\s]+\.js/i,
];
for (const pattern of forbidden) {
if (pattern.test(html)) {
console.error(`Unexpected client JavaScript matched ${pattern}`);
process.exit(1);
}
}
NODE
- name: Verify Cloudflare image resizing URLs
run: |
node <<'NODE'
const { readFileSync } = require('node:fs');
const html = readFileSync('apps/landing-page/out/index.html', 'utf8');
const resizedUrls = html.match(/https:\/\/static\.open-design\.ai\/cdn-cgi\/image\//g) ?? [];
if (resizedUrls.length < 16) {
console.error(`Expected at least 16 Cloudflare resized image URLs, found ${resizedUrls.length}`);
process.exit(1);
}
if (/(?:src|content)=["']\/assets\/[A-Za-z0-9_.-]+\.png/.test(html)) {
console.error('Found local /assets/*.png image reference in generated landing HTML.');
process.exit(1);
}
NODE
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: apps/landing-page
packageManager: npm
command: >
pages deploy out
--project-name=open-design-landing
--branch=${{ github.ref_name }}
+68
View File
@@ -0,0 +1,68 @@
name: github-metrics
on:
schedule:
# Runs daily at 00:15 UTC; output is committed to docs/assets/github-metrics.svg.
- cron: '15 0 * * *'
workflow_dispatch:
push:
branches:
- main
paths:
- .github/workflows/metrics.yml
permissions:
contents: write
pull-requests: write
jobs:
metrics:
name: Generate repository metrics SVG
runs-on: ubuntu-latest
steps:
- name: Generate GitHub repository metrics
uses: lowlighter/metrics@latest
with:
# Output path; the action opens/updates a PR when this file changes.
# Requires manual review to merge. If metrics unchanged, no PR is created.
filename: docs/assets/github-metrics.svg
# Auth: METRICS_TOKEN must be a fine-grained PAT or GitHub App token that
# can create pull requests in this repository. GITHUB_TOKEN is kept only
# as a read/render fallback because many orgs disallow PR creation from it.
token: ${{ secrets.METRICS_TOKEN || secrets.GITHUB_TOKEN }}
committer_token: ${{ secrets.METRICS_TOKEN }}
output_action: pull-request
output_condition: data-changed
# Use the repository template (per-repo metrics, not user metrics).
# Organization-owned repositories must be targeted explicitly, otherwise
# lowlighter/metrics infers the token owner and treats the target as an org.
template: repository
base: ''
user: nexu-io
repo: open-design
# Plugins. Anything that requires a personal token will silently no-op
# without METRICS_TOKEN — the rest still produce a useful SVG.
plugin_contributors: yes
plugin_contributors_categories: |
{
"Skills": "skills/**",
"Design systems": "design-systems/**",
"Web": "apps/web/**",
"Daemon": "apps/daemon/**",
"Docs": "docs/**"
}
plugin_followup: yes
plugin_followup_sections: pr, issue
plugin_languages: yes
plugin_languages_details: lines, percentage
plugin_languages_limit: 8
plugin_lines: yes
plugin_traffic: yes
plugin_stargazers: yes
plugin_stargazers_charts_type: chartist
config_timezone: Asia/Shanghai
config_display: large
@@ -0,0 +1,55 @@
name: refresh-contributors-wall
on:
# Daily refresh keeps the contributors wall CDN cache moving even when
# contributor data changes outside pull request merges.
schedule:
- cron: '0 1 * * *'
# Manual trigger: Use when you need to force-refresh the contributors wall
# outside the daily schedule (e.g., after a bulk contributor update or
# after fixing the cache_bust pattern in README files).
workflow_dispatch:
permissions:
contents: write
pull-requests: write
concurrency:
group: refresh-contributors-wall
cancel-in-progress: true
jobs:
refresh:
name: Refresh contributors wall cache bust
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
- name: Refresh cache bust date
run: |
DATE="$(date -u +%F)"
MATCHES="$(perl -0ne '$count += () = /cache_bust=\d{4}-\d{2}-\d{2}/g; END { print $count + 0 }' README*.md)"
if [ "$MATCHES" -eq 0 ]; then
echo "Warning: No cache_bust patterns found. README format may have changed."
exit 1
fi
perl -0pi -e "s/cache_bust=\d{4}-\d{2}-\d{2}/cache_bust=$DATE/g" README*.md
- name: Create refresh pull request
uses: peter-evans/create-pull-request@v8
with:
# Auth mirrors the metrics workflow: prefer a repository token that can
# create pull requests, with GITHUB_TOKEN as a fallback for repos where
# Actions-created PRs are allowed.
token: ${{ secrets.METRICS_TOKEN || secrets.GITHUB_TOKEN }}
add-paths: 'README*.md'
branch: automation/refresh-contributors-wall
delete-branch: true
commit-message: 'docs(readme): refresh contributors wall'
title: 'docs(readme): refresh contributors wall'
body: |
Refreshes the contributors wall cache bust date in README files.
Generated by the scheduled `refresh-contributors-wall` workflow.
+467
View File
@@ -0,0 +1,467 @@
name: release-beta
on:
workflow_dispatch:
inputs:
signed:
description: "Build signed/notarized mac artifacts. Disable only for explicit unsigned validation releases."
required: true
type: boolean
default: true
permissions:
contents: write
concurrency:
group: open-design-release-beta
cancel-in-progress: false
jobs:
metadata:
name: Prepare beta metadata
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
OPEN_DESIGN_RELEASE_SIGNED: ${{ inputs.signed }}
outputs:
asset_version_suffix: ${{ steps.beta.outputs.asset_version_suffix }}
base_version: ${{ steps.beta.outputs.base_version }}
beta_tag: ${{ steps.beta.outputs.beta_tag }}
beta_version: ${{ steps.beta.outputs.beta_version }}
branch: ${{ steps.beta.outputs.branch }}
commit: ${{ steps.beta.outputs.commit }}
release_name: ${{ steps.beta.outputs.release_name }}
signed: ${{ steps.beta.outputs.signed }}
version_tag: ${{ steps.beta.outputs.version_tag }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Prepare beta release metadata
id: beta
run: node --experimental-strip-types ./scripts/release-beta.ts
build_mac:
name: Build beta mac arm64
needs: metadata
runs-on: macos-14
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Apply beta package version
run: npm pkg set "version=${{ needs.metadata.outputs.beta_version }}" --prefix apps/packaged
- name: Prepare Apple signing certificate
if: ${{ inputs.signed }}
env:
APPLE_SIGNING_CERTIFICATE_BASE64: ${{ secrets.APPLE_SIGNING_CERTIFICATE_BASE64 }}
APPLE_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }}
run: |
set -euo pipefail
cert_path="$RUNNER_TEMP/open-design-signing.p12"
if ! printf '%s' "$APPLE_SIGNING_CERTIFICATE_BASE64" | base64 --decode > "$cert_path" 2>/dev/null; then
printf '%s' "$APPLE_SIGNING_CERTIFICATE_BASE64" | base64 -D > "$cert_path"
fi
{
echo "CSC_LINK=$cert_path"
echo "CSC_KEY_PASSWORD=$APPLE_SIGNING_CERTIFICATE_PASSWORD"
} >> "$GITHUB_ENV"
- name: Build beta mac artifacts
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
set -euo pipefail
signed_flag=""
if [ "${{ inputs.signed }}" = "true" ]; then
signed_flag="--signed"
fi
pnpm exec tools-pack mac build \
--dir "$RUNNER_TEMP/tools-pack" \
--namespace release-beta \
--portable \
--to all \
--json \
$signed_flag
- name: Prepare beta assets
id: assets
run: |
set -euo pipefail
release_dir="$RUNNER_TEMP/release-assets"
mkdir -p "$release_dir"
source_dmg="$RUNNER_TEMP/tools-pack/out/mac/namespaces/release-beta/dmg/Open Design-release-beta.dmg"
source_zip="$RUNNER_TEMP/tools-pack/out/mac/namespaces/release-beta/zip/Open Design-release-beta.zip"
if [ ! -f "$source_dmg" ]; then
echo "expected dmg not found at $source_dmg" >&2
exit 1
fi
if [ ! -f "$source_zip" ]; then
echo "expected zip not found at $source_zip" >&2
exit 1
fi
asset_suffix="${{ needs.metadata.outputs.asset_version_suffix }}"
versioned_dmg="open-design-${{ needs.metadata.outputs.beta_version }}${asset_suffix}-mac-arm64.dmg"
versioned_zip="open-design-${{ needs.metadata.outputs.beta_version }}${asset_suffix}-mac-arm64.zip"
dmg_checksum_file="$versioned_dmg.sha256"
zip_checksum_file="$versioned_zip.sha256"
cp "$source_dmg" "$release_dir/$versioned_dmg"
cp "$source_zip" "$release_dir/$versioned_zip"
(
cd "$release_dir"
shasum -a 256 "$versioned_dmg" > "$dmg_checksum_file"
shasum -a 256 "$versioned_zip" > "$zip_checksum_file"
)
zip_sha512="$(openssl dgst -sha512 -binary "$release_dir/$versioned_zip" | openssl base64 -A)"
zip_size="$(stat -f%z "$release_dir/$versioned_zip")"
zip_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${{ needs.metadata.outputs.version_tag }}/$versioned_zip"
release_date="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
cat > "$release_dir/latest-mac.yml" <<EOF
version: "${{ needs.metadata.outputs.beta_version }}"
files:
- url: "$zip_url"
sha512: "$zip_sha512"
size: $zip_size
path: "$zip_url"
sha512: "$zip_sha512"
releaseDate: "$release_date"
releaseNotes: "Open Design beta ${{ needs.metadata.outputs.beta_version }}${asset_suffix}"
EOF
- name: Upload mac release bundle
uses: actions/upload-artifact@v7
with:
name: open-design-beta-mac-release-assets
path: ${{ runner.temp }}/release-assets
build_win:
name: Build beta win x64
needs: metadata
runs-on: windows-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Apply beta package version
run: npm pkg set "version=${{ needs.metadata.outputs.beta_version }}" --prefix apps/packaged
- name: Build beta windows artifacts
shell: pwsh
run: >-
pnpm exec tools-pack win build
--dir "${{ runner.temp }}/tools-pack"
--namespace release-beta-win
--portable
--to nsis
--json
- name: Prepare windows beta assets
shell: pwsh
run: |
$releaseDir = Join-Path $env:RUNNER_TEMP "release-assets"
New-Item -ItemType Directory -Force -Path $releaseDir | Out-Null
$sourceInstaller = Join-Path $env:RUNNER_TEMP "tools-pack/out/win/namespaces/release-beta-win/builder/Open Design-release-beta-win-setup.exe"
$sourceBlockmap = Join-Path $env:RUNNER_TEMP "tools-pack/out/win/namespaces/release-beta-win/builder/Open Design-release-beta-win-setup.exe.blockmap"
if (!(Test-Path $sourceInstaller)) {
throw "expected installer not found at $sourceInstaller"
}
if (!(Test-Path $sourceBlockmap)) {
throw "expected blockmap not found at $sourceBlockmap"
}
$windowsAssetSuffix = ".unsigned"
$versionedInstaller = "open-design-${{ needs.metadata.outputs.beta_version }}$windowsAssetSuffix-win-x64-setup.exe"
$versionedBlockmap = "open-design-${{ needs.metadata.outputs.beta_version }}$windowsAssetSuffix-win-x64-setup.exe.blockmap"
$checksumFile = "$versionedInstaller.sha256"
Copy-Item $sourceInstaller (Join-Path $releaseDir $versionedInstaller)
Copy-Item $sourceBlockmap (Join-Path $releaseDir $versionedBlockmap)
$installerPath = Join-Path $releaseDir $versionedInstaller
$hash = (Get-FileHash -Path $installerPath -Algorithm SHA256).Hash.ToLowerInvariant()
"$hash $versionedInstaller" | Set-Content -Path (Join-Path $releaseDir $checksumFile)
$installerBytes = [System.IO.File]::ReadAllBytes($installerPath)
$installerSha512 = [System.Convert]::ToBase64String([System.Security.Cryptography.SHA512]::Create().ComputeHash($installerBytes))
$installerSize = (Get-Item $installerPath).Length
$installerUrl = "https://github.com/$env:GITHUB_REPOSITORY/releases/download/${{ needs.metadata.outputs.version_tag }}/$versionedInstaller"
$releaseDate = [DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
@(
'version: "${{ needs.metadata.outputs.beta_version }}"'
'files:'
" - url: `"$installerUrl`""
" sha512: `"$installerSha512`""
" size: $installerSize"
"path: `"$installerUrl`""
"sha512: `"$installerSha512`""
"releaseDate: `"$releaseDate`""
"releaseNotes: `"Open Design beta ${{ needs.metadata.outputs.beta_version }}$windowsAssetSuffix`""
) | Set-Content -Path (Join-Path $releaseDir "latest.yml")
- name: Upload windows release bundle
uses: actions/upload-artifact@v7
with:
name: open-design-beta-win-release-assets
path: ${{ runner.temp }}/release-assets
build_linux:
name: Build beta linux x64
needs: metadata
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Apply beta package version
env:
BETA_VERSION: ${{ needs.metadata.outputs.beta_version }}
run: npm pkg set "version=$BETA_VERSION" --prefix apps/packaged
# `--containerized` builds the AppImage inside the electronuserland/builder
# Docker image (glibc 2.27 baseline) so the resulting binary runs on older
# distros than ubuntu-latest's glibc 2.39. Docker is preinstalled on the
# GitHub-hosted ubuntu-latest runner, so no extra setup is required.
- name: Build beta linux artifacts
run: |
set -euo pipefail
pnpm exec tools-pack linux build \
--dir "$RUNNER_TEMP/tools-pack" \
--namespace release-beta-linux \
--portable \
--to appimage \
--containerized \
--json
- name: Prepare linux beta assets
env:
BETA_VERSION: ${{ needs.metadata.outputs.beta_version }}
run: |
set -euo pipefail
release_dir="$RUNNER_TEMP/release-assets"
mkdir -p "$release_dir"
source_appimage="$RUNNER_TEMP/tools-pack/out/linux/namespaces/release-beta-linux/builder/Open Design-release-beta-linux.AppImage"
if [ ! -f "$source_appimage" ]; then
echo "expected AppImage not found at $source_appimage" >&2
exit 1
fi
# Linux currently has no signing path in tools-pack, so the suffix is
# hardcoded to .unsigned (matching the windows convention above).
linux_asset_suffix=".unsigned"
versioned_appimage="open-design-${BETA_VERSION}${linux_asset_suffix}-linux-x64.AppImage"
checksum_file="$versioned_appimage.sha256"
cp "$source_appimage" "$release_dir/$versioned_appimage"
(
cd "$release_dir"
sha256sum "$versioned_appimage" > "$checksum_file"
)
- name: Upload linux release bundle
uses: actions/upload-artifact@v7
with:
name: open-design-beta-linux-release-assets
path: ${{ runner.temp }}/release-assets
publish:
name: Publish beta release
needs:
- metadata
- build_mac
- build_win
- build_linux
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Download mac release bundle
uses: actions/download-artifact@v8
with:
name: open-design-beta-mac-release-assets
path: ${{ runner.temp }}/release-assets/mac
- name: Download windows release bundle
uses: actions/download-artifact@v8
with:
name: open-design-beta-win-release-assets
path: ${{ runner.temp }}/release-assets/win
- name: Download linux release bundle
uses: actions/download-artifact@v8
with:
name: open-design-beta-linux-release-assets
path: ${{ runner.temp }}/release-assets/linux
- name: Move beta tags to current commit
run: |
set -euo pipefail
git tag -f "${{ needs.metadata.outputs.version_tag }}" "$GITHUB_SHA"
git push origin "refs/tags/${{ needs.metadata.outputs.version_tag }}" --force
git tag -f "${{ needs.metadata.outputs.beta_tag }}" "$GITHUB_SHA"
git push origin "refs/tags/${{ needs.metadata.outputs.beta_tag }}" --force
- name: Write release notes
id: notes
run: |
set -euo pipefail
version_notes_file="$RUNNER_TEMP/open-design-beta-version-notes.md"
latest_notes_file="$RUNNER_TEMP/open-design-beta-latest-notes.md"
cat > "$version_notes_file" <<EOF
## Summary
- channel: beta
- version: ${{ needs.metadata.outputs.beta_version }}
- base version: ${{ needs.metadata.outputs.base_version }}
- mac signed/notarized: ${{ needs.metadata.outputs.signed }}
- windows signed: false
- branch: ${{ needs.metadata.outputs.branch }}
- commit: ${{ needs.metadata.outputs.commit }}
This beta release ships mac arm64 DMG/update ZIP, Windows x64 NSIS installer assets, Linux x64 AppImage (no auto-update yet), checksums, and updater feed files.
EOF
cat > "$latest_notes_file" <<EOF
## Summary
- channel: beta
- latest version: ${{ needs.metadata.outputs.beta_version }}
- latest tag: ${{ needs.metadata.outputs.version_tag }}
This release is the mutable beta channel feed carrier. It should contain feed assets only: latest-mac.yml and latest.yml.
EOF
{
echo "version_notes_file=$version_notes_file"
echo "latest_notes_file=$latest_notes_file"
} >> "$GITHUB_OUTPUT"
- name: Create or update immutable beta prerelease
run: |
set -euo pipefail
release_dir="$RUNNER_TEMP/release-assets/mac"
all_release_dir="$RUNNER_TEMP/release-assets/all"
mkdir -p "$all_release_dir"
cp "$RUNNER_TEMP/release-assets/mac"/* "$all_release_dir/"
cp "$RUNNER_TEMP/release-assets/win"/* "$all_release_dir/"
cp "$RUNNER_TEMP/release-assets/linux"/* "$all_release_dir/"
if gh release view "${{ needs.metadata.outputs.version_tag }}" >/dev/null 2>&1; then
gh release edit "${{ needs.metadata.outputs.version_tag }}" \
--title "${{ needs.metadata.outputs.release_name }}" \
--notes-file "${{ steps.notes.outputs.version_notes_file }}" \
--prerelease
else
gh release create "${{ needs.metadata.outputs.version_tag }}" \
--target "$GITHUB_SHA" \
--title "${{ needs.metadata.outputs.release_name }}" \
--notes-file "${{ steps.notes.outputs.version_notes_file }}" \
--prerelease
fi
gh release upload "${{ needs.metadata.outputs.version_tag }}" "$all_release_dir"/* --clobber
- name: Create or update beta channel feed
run: |
set -euo pipefail
latest_mac_path="$RUNNER_TEMP/release-assets/mac/latest-mac.yml"
latest_win_path="$RUNNER_TEMP/release-assets/win/latest.yml"
if gh release view "${{ needs.metadata.outputs.beta_tag }}" >/dev/null 2>&1; then
while IFS= read -r asset_name; do
if [ -n "$asset_name" ]; then
gh release delete-asset "${{ needs.metadata.outputs.beta_tag }}" "$asset_name" --yes
fi
done < <(gh release view "${{ needs.metadata.outputs.beta_tag }}" --json assets --jq '.assets[].name')
gh release edit "${{ needs.metadata.outputs.beta_tag }}" \
--title "Open Design Beta Latest" \
--notes-file "${{ steps.notes.outputs.latest_notes_file }}" \
--prerelease
gh release upload "${{ needs.metadata.outputs.beta_tag }}" "$latest_mac_path" "$latest_win_path" --clobber
else
gh release create "${{ needs.metadata.outputs.beta_tag }}" \
"$latest_mac_path" "$latest_win_path" \
--target "$GITHUB_SHA" \
--title "Open Design Beta Latest" \
--notes-file "${{ steps.notes.outputs.latest_notes_file }}" \
--prerelease
fi
- name: Publish summary
run: |
{
echo "## Beta release"
echo "- Channel: beta"
echo "- Version: ${{ needs.metadata.outputs.beta_version }}"
echo "- Version tag: ${{ needs.metadata.outputs.version_tag }}"
echo "- Channel feed tag: ${{ needs.metadata.outputs.beta_tag }}"
echo "- mac signed/notarized: ${{ needs.metadata.outputs.signed }}"
echo "- windows signed: false"
echo "- mac assets: open-design-${{ needs.metadata.outputs.beta_version }}${{ needs.metadata.outputs.asset_version_suffix }}-mac-arm64.dmg, open-design-${{ needs.metadata.outputs.beta_version }}${{ needs.metadata.outputs.asset_version_suffix }}-mac-arm64.zip"
echo "- win assets: open-design-${{ needs.metadata.outputs.beta_version }}.unsigned-win-x64-setup.exe, open-design-${{ needs.metadata.outputs.beta_version }}.unsigned-win-x64-setup.exe.blockmap"
echo "- linux assets: open-design-${{ needs.metadata.outputs.beta_version }}.unsigned-linux-x64.AppImage"
echo "- Feeds: latest-mac.yml, latest.yml (no latest-linux.yml; AppImage updater not yet wired)"
} >> "$GITHUB_STEP_SUMMARY"
+486
View File
@@ -0,0 +1,486 @@
name: release-stable
on:
workflow_dispatch:
inputs:
mac_signed:
description: "Build signed/notarized mac artifacts. Disable only for explicit unsigned validation releases."
required: true
type: boolean
default: true
permissions:
contents: write
concurrency:
group: open-design-release-stable
cancel-in-progress: false
jobs:
metadata:
name: Prepare stable metadata
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
outputs:
base_version: ${{ steps.stable.outputs.base_version }}
branch: ${{ steps.stable.outputs.branch }}
commit: ${{ steps.stable.outputs.commit }}
mac_signed: ${{ inputs.mac_signed }}
previous_stable: ${{ steps.stable.outputs.previous_stable }}
release_name: ${{ steps.stable.outputs.release_name }}
stable_version: ${{ steps.stable.outputs.stable_version }}
version_tag: ${{ steps.stable.outputs.version_tag }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Prepare stable release metadata
id: stable
run: node --experimental-strip-types ./scripts/release-stable.ts
verify:
name: Verify build (typecheck + tests)
needs: metadata
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Install dependencies
run: pnpm install --frozen-lockfile
# `scripts/postinstall.mjs` auto-builds `packages/*` and `tools/*`, but
# `apps/daemon` and `apps/desktop` are not in that list. On a fresh clone
# (every CI run), workspace typecheck fails because:
# - `e2e/scripts/runtime-adapter.e2e.live.test.ts` imports types from
# `apps/daemon/dist/*.js`
# - `apps/packaged/src/index.ts` dynamic-imports `@open-design/desktop/main`
# which resolves to `apps/desktop/dist/main/index.d.ts`
# Build them explicitly here. Keeps the root `typecheck` script untouched.
- name: Build daemon and desktop (typecheck dependencies)
run: |
pnpm --filter @open-design/daemon build
pnpm --filter @open-design/desktop build
- name: Typecheck workspaces
run: pnpm -r --workspace-concurrency=1 --if-present run typecheck
- name: Check residual JS in TypeScript packages
run: pnpm check:residual-js
# Workspace tests are intentionally not gated here. apps/web's
# i18n content-coverage tests assert that every locale carries
# display metadata for every prompt template / skill / design
# system. Those tests fail on `main` as of this writing because
# PR #187 added two new prompt templates without translating
# their metadata into the 9 ship-ready locales — an i18n drift
# that's out of scope for the release infrastructure. Tracked as
# a follow-up; revisit once locale metadata is back in sync.
build_mac:
name: Build stable mac arm64
needs: [metadata, verify]
runs-on: macos-14
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Prepare Apple signing certificate
if: ${{ inputs.mac_signed }}
env:
APPLE_SIGNING_CERTIFICATE_BASE64: ${{ secrets.APPLE_SIGNING_CERTIFICATE_BASE64 }}
APPLE_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_SIGNING_CERTIFICATE_PASSWORD }}
run: |
set -euo pipefail
cert_path="$RUNNER_TEMP/open-design-signing.p12"
if ! printf '%s' "$APPLE_SIGNING_CERTIFICATE_BASE64" | base64 --decode > "$cert_path" 2>/dev/null; then
printf '%s' "$APPLE_SIGNING_CERTIFICATE_BASE64" | base64 -D > "$cert_path"
fi
{
echo "CSC_LINK=$cert_path"
echo "CSC_KEY_PASSWORD=$APPLE_SIGNING_CERTIFICATE_PASSWORD"
} >> "$GITHUB_ENV"
- name: Build stable mac artifacts
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
set -euo pipefail
signed_flag=""
if [ "${{ inputs.mac_signed }}" = "true" ]; then
signed_flag="--signed"
fi
pnpm exec tools-pack mac build \
--dir "$RUNNER_TEMP/tools-pack" \
--namespace release-stable \
--portable \
--to all \
--json \
$signed_flag
- name: Prepare stable mac assets
id: assets
run: |
set -euo pipefail
release_dir="$RUNNER_TEMP/release-assets"
mkdir -p "$release_dir"
source_dmg="$RUNNER_TEMP/tools-pack/out/mac/namespaces/release-stable/dmg/Open Design-release-stable.dmg"
source_zip="$RUNNER_TEMP/tools-pack/out/mac/namespaces/release-stable/zip/Open Design-release-stable.zip"
if [ ! -f "$source_dmg" ]; then
echo "expected dmg not found at $source_dmg" >&2
exit 1
fi
if [ ! -f "$source_zip" ]; then
echo "expected zip not found at $source_zip" >&2
exit 1
fi
versioned_dmg="open-design-${{ needs.metadata.outputs.stable_version }}-mac-arm64.dmg"
versioned_zip="open-design-${{ needs.metadata.outputs.stable_version }}-mac-arm64.zip"
dmg_checksum_file="$versioned_dmg.sha256"
zip_checksum_file="$versioned_zip.sha256"
cp "$source_dmg" "$release_dir/$versioned_dmg"
cp "$source_zip" "$release_dir/$versioned_zip"
(
cd "$release_dir"
shasum -a 256 "$versioned_dmg" > "$dmg_checksum_file"
shasum -a 256 "$versioned_zip" > "$zip_checksum_file"
)
zip_sha512="$(openssl dgst -sha512 -binary "$release_dir/$versioned_zip" | openssl base64 -A)"
zip_size="$(stat -f%z "$release_dir/$versioned_zip")"
zip_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${{ needs.metadata.outputs.version_tag }}/$versioned_zip"
release_date="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
cat > "$release_dir/latest-mac.yml" <<EOF
version: "${{ needs.metadata.outputs.stable_version }}"
files:
- url: "$zip_url"
sha512: "$zip_sha512"
size: $zip_size
path: "$zip_url"
sha512: "$zip_sha512"
releaseDate: "$release_date"
releaseNotes: "Open Design ${{ needs.metadata.outputs.stable_version }}"
EOF
- name: Upload mac release bundle
uses: actions/upload-artifact@v7
with:
name: open-design-stable-mac-release-assets
path: ${{ runner.temp }}/release-assets
build_win:
name: Build stable win x64
needs: [metadata, verify]
runs-on: windows-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build stable windows artifacts
shell: pwsh
run: >-
pnpm exec tools-pack win build
--dir "${{ runner.temp }}/tools-pack"
--namespace release-stable-win
--portable
--to nsis
--json
- name: Prepare windows stable assets
shell: pwsh
run: |
$releaseDir = Join-Path $env:RUNNER_TEMP "release-assets"
New-Item -ItemType Directory -Force -Path $releaseDir | Out-Null
$sourceInstaller = Join-Path $env:RUNNER_TEMP "tools-pack/out/win/namespaces/release-stable-win/builder/Open Design-release-stable-win-setup.exe"
$sourceBlockmap = Join-Path $env:RUNNER_TEMP "tools-pack/out/win/namespaces/release-stable-win/builder/Open Design-release-stable-win-setup.exe.blockmap"
if (!(Test-Path $sourceInstaller)) {
throw "expected installer not found at $sourceInstaller"
}
if (!(Test-Path $sourceBlockmap)) {
throw "expected blockmap not found at $sourceBlockmap"
}
$versionedInstaller = "open-design-${{ needs.metadata.outputs.stable_version }}-win-x64-setup.exe"
$versionedBlockmap = "open-design-${{ needs.metadata.outputs.stable_version }}-win-x64-setup.exe.blockmap"
$checksumFile = "$versionedInstaller.sha256"
Copy-Item $sourceInstaller (Join-Path $releaseDir $versionedInstaller)
Copy-Item $sourceBlockmap (Join-Path $releaseDir $versionedBlockmap)
$installerPath = Join-Path $releaseDir $versionedInstaller
$hash = (Get-FileHash -Path $installerPath -Algorithm SHA256).Hash.ToLowerInvariant()
"$hash $versionedInstaller" | Set-Content -Path (Join-Path $releaseDir $checksumFile)
$installerBytes = [System.IO.File]::ReadAllBytes($installerPath)
$installerSha512 = [System.Convert]::ToBase64String([System.Security.Cryptography.SHA512]::Create().ComputeHash($installerBytes))
$installerSize = (Get-Item $installerPath).Length
$installerUrl = "https://github.com/$env:GITHUB_REPOSITORY/releases/download/${{ needs.metadata.outputs.version_tag }}/$versionedInstaller"
$releaseDate = [DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
@(
'version: "${{ needs.metadata.outputs.stable_version }}"'
'files:'
" - url: `"$installerUrl`""
" sha512: `"$installerSha512`""
" size: $installerSize"
"path: `"$installerUrl`""
"sha512: `"$installerSha512`""
"releaseDate: `"$releaseDate`""
"releaseNotes: `"Open Design ${{ needs.metadata.outputs.stable_version }}`""
) | Set-Content -Path (Join-Path $releaseDir "latest.yml")
- name: Upload windows release bundle
uses: actions/upload-artifact@v7
with:
name: open-design-stable-win-release-assets
path: ${{ runner.temp }}/release-assets
build_linux:
name: Build stable linux x64
needs: [metadata, verify]
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Setup pnpm
uses: pnpm/action-setup@v5
with:
version: 10.33.2
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: 24
- name: Install dependencies
run: pnpm install --frozen-lockfile
# `--containerized` builds the AppImage inside the electronuserland/builder
# Docker image (glibc 2.27 baseline) so the resulting binary runs on older
# distros than ubuntu-latest's glibc 2.39. Docker is preinstalled on the
# GitHub-hosted ubuntu-latest runner, so no extra setup is required.
- name: Build stable linux artifacts
run: |
set -euo pipefail
pnpm exec tools-pack linux build \
--dir "$RUNNER_TEMP/tools-pack" \
--namespace release-stable-linux \
--portable \
--to appimage \
--containerized \
--json
- name: Prepare linux stable assets
env:
STABLE_VERSION: ${{ needs.metadata.outputs.stable_version }}
run: |
set -euo pipefail
release_dir="$RUNNER_TEMP/release-assets"
mkdir -p "$release_dir"
source_appimage="$RUNNER_TEMP/tools-pack/out/linux/namespaces/release-stable-linux/builder/Open Design-release-stable-linux.AppImage"
if [ ! -f "$source_appimage" ]; then
echo "expected AppImage not found at $source_appimage" >&2
exit 1
fi
# Linux currently has no signing path in tools-pack; the asset has no
# signing-related suffix (matches the windows convention above).
versioned_appimage="open-design-${STABLE_VERSION}-linux-x64.AppImage"
checksum_file="$versioned_appimage.sha256"
cp "$source_appimage" "$release_dir/$versioned_appimage"
(
cd "$release_dir"
sha256sum "$versioned_appimage" > "$checksum_file"
)
- name: Upload linux release bundle
uses: actions/upload-artifact@v7
with:
name: open-design-stable-linux-release-assets
path: ${{ runner.temp }}/release-assets
publish:
name: Publish stable release
needs:
- metadata
- verify
- build_mac
- build_win
- build_linux
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
- name: Checkout
uses: actions/checkout@v6.0.2
with:
fetch-depth: 0
- name: Pre-flight tag/release check
run: |
set -euo pipefail
if git ls-remote --exit-code --tags origin "refs/tags/${{ needs.metadata.outputs.version_tag }}" >/dev/null 2>&1; then
echo "tag ${{ needs.metadata.outputs.version_tag }} already exists on origin; aborting" >&2
exit 1
fi
if gh release view "${{ needs.metadata.outputs.version_tag }}" >/dev/null 2>&1; then
echo "release ${{ needs.metadata.outputs.version_tag }} already exists; aborting" >&2
exit 1
fi
- name: Download mac release bundle
uses: actions/download-artifact@v8
with:
name: open-design-stable-mac-release-assets
path: ${{ runner.temp }}/release-assets/mac
- name: Download windows release bundle
uses: actions/download-artifact@v8
with:
name: open-design-stable-win-release-assets
path: ${{ runner.temp }}/release-assets/win
- name: Download linux release bundle
uses: actions/download-artifact@v8
with:
name: open-design-stable-linux-release-assets
path: ${{ runner.temp }}/release-assets/linux
- name: Write release notes shell
id: notes
run: |
set -euo pipefail
notes_file="$RUNNER_TEMP/open-design-stable-notes.md"
cat > "$notes_file" <<EOF
## Summary
- channel: stable
- version: ${{ needs.metadata.outputs.stable_version }}
- mac signed/notarized: ${{ inputs.mac_signed }}
- windows signed: false
- branch: ${{ needs.metadata.outputs.branch }}
- commit: ${{ needs.metadata.outputs.commit }}
See [CHANGELOG.md](https://github.com/${GITHUB_REPOSITORY}/blob/${{ needs.metadata.outputs.version_tag }}/CHANGELOG.md) for the full release notes.
This stable release ships mac arm64 DMG/update ZIP, Windows x64 NSIS installer assets, Linux x64 AppImage (no auto-update yet), checksums, and updater feed files.
EOF
echo "notes_file=$notes_file" >> "$GITHUB_OUTPUT"
- name: Create draft release with tag
id: create_release
run: |
set -euo pipefail
# gh release create creates the tag at $GITHUB_SHA atomically with the release.
# Using --draft keeps the release invisible until all assets upload successfully;
# the cleanup step rolls back the release + tag together if any subsequent step fails.
gh release create "${{ needs.metadata.outputs.version_tag }}" \
--target "$GITHUB_SHA" \
--title "${{ needs.metadata.outputs.release_name }}" \
--notes-file "${{ steps.notes.outputs.notes_file }}" \
--draft
- name: Upload assets to draft release
run: |
set -euo pipefail
all_release_dir="$RUNNER_TEMP/release-assets/all"
mkdir -p "$all_release_dir"
cp "$RUNNER_TEMP/release-assets/mac"/* "$all_release_dir/"
cp "$RUNNER_TEMP/release-assets/win"/* "$all_release_dir/"
cp "$RUNNER_TEMP/release-assets/linux"/* "$all_release_dir/"
gh release upload "${{ needs.metadata.outputs.version_tag }}" "$all_release_dir"/*
- name: Promote draft to published latest
run: |
set -euo pipefail
gh release edit "${{ needs.metadata.outputs.version_tag }}" \
--draft=false \
--latest
- name: Cleanup release + tag on failure
if: failure() && steps.create_release.outcome == 'success'
run: |
set +e
echo "publish failed after release was created; rolling back release and tag"
gh release delete "${{ needs.metadata.outputs.version_tag }}" --cleanup-tag --yes
# belt-and-suspenders: ensure remote tag is gone even if --cleanup-tag missed
git push origin --delete "refs/tags/${{ needs.metadata.outputs.version_tag }}" || true
- name: Publish summary
run: |
{
echo "## Stable release"
echo "- Channel: stable"
echo "- Version: ${{ needs.metadata.outputs.stable_version }}"
echo "- Version tag: ${{ needs.metadata.outputs.version_tag }}"
echo "- mac signed/notarized: ${{ inputs.mac_signed }}"
echo "- windows signed: false"
echo "- mac assets: open-design-${{ needs.metadata.outputs.stable_version }}-mac-arm64.dmg, open-design-${{ needs.metadata.outputs.stable_version }}-mac-arm64.zip"
echo "- win assets: open-design-${{ needs.metadata.outputs.stable_version }}-win-x64-setup.exe, open-design-${{ needs.metadata.outputs.stable_version }}-win-x64-setup.exe.blockmap"
echo "- linux assets: open-design-${{ needs.metadata.outputs.stable_version }}-linux-x64.AppImage"
echo "- Feeds: latest-mac.yml, latest.yml (no latest-linux.yml; AppImage updater not yet wired)"
} >> "$GITHUB_STEP_SUMMARY"
+4 -4
View File
@@ -698,7 +698,7 @@ Vollständiger Walkthrough, Merge-Messlatte, Code Style und was wir nicht annehm
Danke an alle, die Open Design vorangebracht haben: durch Code, Docs, Feedback, neue Skills, neue Design Systems oder auch ein scharfes Issue. Jeder echte Beitrag zählt, und die Wand unten ist die einfachste Art, das laut zu sagen. Danke an alle, die Open Design vorangebracht haben: durch Code, Docs, Feedback, neue Skills, neue Design Systems oder auch ein scharfes Issue. Jeder echte Beitrag zählt, und die Wand unten ist die einfachste Art, das laut zu sagen.
<a href="https://github.com/nexu-io/open-design/graphs/contributors"> <a href="https://github.com/nexu-io/open-design/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-05-04" alt="Open Design contributors" /> <img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-09-30" alt="Open Design contributors" />
</a> </a>
Wenn Sie Ihren ersten PR gemergt haben: willkommen. Das Label [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) ist der Einstiegspunkt. Wenn Sie Ihren ersten PR gemergt haben: willkommen. Das Label [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) ist der Einstiegspunkt.
@@ -715,9 +715,9 @@ Das SVG oben wird täglich von [`.github/workflows/metrics.yml`](.github/workflo
<a href="https://star-history.com/#nexu-io/open-design&Date"> <a href="https://star-history.com/#nexu-io/open-design&Date">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-09-30" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
<img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
</picture> </picture>
</a> </a>
+4 -4
View File
@@ -708,7 +708,7 @@ Guide complet, critères de merge, style de code et refus fréquents → [`CONTR
Merci à toutes les personnes qui font avancer Open Design : code, docs, retours, nouveaux Skills, nouveaux Design Systems ou issues bien ciblées. Chaque vraie contribution compte. Merci à toutes les personnes qui font avancer Open Design : code, docs, retours, nouveaux Skills, nouveaux Design Systems ou issues bien ciblées. Chaque vraie contribution compte.
<a href="https://github.com/nexu-io/open-design/graphs/contributors"> <a href="https://github.com/nexu-io/open-design/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-05-04" alt="Contributeurs Open Design" /> <img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-09-30" alt="Contributeurs Open Design" />
</a> </a>
Si vous avez livré votre première PR, bienvenue. Le label [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) est le point d’entrée. Si vous avez livré votre première PR, bienvenue. Le label [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) est le point d’entrée.
@@ -725,9 +725,9 @@ Le SVG ci-dessus est régénéré chaque jour par [`.github/workflows/metrics.ym
<a href="https://star-history.com/#nexu-io/open-design&Date"> <a href="https://star-history.com/#nexu-io/open-design&Date">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-09-30" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
<img alt="Historique des stars Open Design" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <img alt="Historique des stars Open Design" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
</picture> </picture>
</a> </a>
+4 -4
View File
@@ -695,7 +695,7 @@ Issue、PR、新 Skill、新 Design System を歓迎します。最も効果の
コード、ドキュメント、フィードバック、新 Skill、新 Design System、あるいは鋭い Issue — あらゆる形で Open Design を前進させてくださったすべての方に感謝します。すべての実質的なコントリビューションは大切であり、以下のウォールは最もシンプルな感謝の表明です。 コード、ドキュメント、フィードバック、新 Skill、新 Design System、あるいは鋭い Issue — あらゆる形で Open Design を前進させてくださったすべての方に感謝します。すべての実質的なコントリビューションは大切であり、以下のウォールは最もシンプルな感謝の表明です。
<a href="https://github.com/nexu-io/open-design/graphs/contributors"> <a href="https://github.com/nexu-io/open-design/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-05-04" alt="Open Design コントリビューター" /> <img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-09-30" alt="Open Design コントリビューター" />
</a> </a>
初めての PR を送った方 — ようこそ。[`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) ラベルがエントリポイントです。 初めての PR を送った方 — ようこそ。[`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) ラベルがエントリポイントです。
@@ -712,9 +712,9 @@ Issue、PR、新 Skill、新 Design System を歓迎します。最も効果の
<a href="https://star-history.com/#nexu-io/open-design&Date"> <a href="https://star-history.com/#nexu-io/open-design&Date">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-09-30" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
<img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
</picture> </picture>
</a> </a>
+4 -4
View File
@@ -696,7 +696,7 @@ daemon 부팅 시 `PATH`에서 자동 감지됩니다. 설정 필요 없음. 스
Open Design을 앞으로 나아가게 도와준 모든 분께 감사드립니다 — 코드, 문서, 피드백, 새 skill, 새 디자인 시스템, 또는 날카로운 이슈 하나라도. 모든 진짜 기여가 의미 있고, 아래의 벽이 가장 직접적인 "감사합니다"입니다. Open Design을 앞으로 나아가게 도와준 모든 분께 감사드립니다 — 코드, 문서, 피드백, 새 skill, 새 디자인 시스템, 또는 날카로운 이슈 하나라도. 모든 진짜 기여가 의미 있고, 아래의 벽이 가장 직접적인 "감사합니다"입니다.
<a href="https://github.com/nexu-io/open-design/graphs/contributors"> <a href="https://github.com/nexu-io/open-design/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-05-04" alt="Open Design 컨트리뷰터" /> <img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-09-30" alt="Open Design 컨트리뷰터" />
</a> </a>
첫 PR을 보냈다면 — 환영합니다. [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) 레이블이 시작점입니다. 첫 PR을 보냈다면 — 환영합니다. [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) 레이블이 시작점입니다.
@@ -713,9 +713,9 @@ Open Design을 앞으로 나아가게 도와준 모든 분께 감사드립니다
<a href="https://star-history.com/#nexu-io/open-design&Date"> <a href="https://star-history.com/#nexu-io/open-design&Date">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-09-30" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
<img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
</picture> </picture>
</a> </a>
+4 -4
View File
@@ -716,7 +716,7 @@ Full walkthrough, bar-for-merging, code style, and what we don't accept → [`CO
Thanks to everyone who has helped move Open Design forward — through code, docs, feedback, new skills, new design systems, or even a sharp issue. Every real contribution counts, and the wall below is the easiest way to say so out loud. Thanks to everyone who has helped move Open Design forward — through code, docs, feedback, new skills, new design systems, or even a sharp issue. Every real contribution counts, and the wall below is the easiest way to say so out loud.
<a href="https://github.com/nexu-io/open-design/graphs/contributors"> <a href="https://github.com/nexu-io/open-design/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-05-04" alt="Open Design contributors" /> <img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-09-30" alt="Open Design contributors" />
</a> </a>
If you've shipped your first PR — welcome. The [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) label is the entry point. If you've shipped your first PR — welcome. The [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) label is the entry point.
@@ -733,9 +733,9 @@ The SVG above is regenerated daily by [`.github/workflows/metrics.yml`](.github/
<a href="https://star-history.com/#nexu-io/open-design&Date"> <a href="https://star-history.com/#nexu-io/open-design&Date">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-09-30" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
<img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
</picture> </picture>
</a> </a>
+4 -4
View File
@@ -704,7 +704,7 @@ Issues, PR, новые skills и новые design systems приветству
Спасибо всем, кто помогает двигать Open Design вперёд — кодом, документацией, обратной связью, новыми skills, новыми design systems или просто точным issue. Вклад любого реального масштаба здесь важен, а стена ниже — самый простой способ сказать это вслух. Спасибо всем, кто помогает двигать Open Design вперёд — кодом, документацией, обратной связью, новыми skills, новыми design systems или просто точным issue. Вклад любого реального масштаба здесь важен, а стена ниже — самый простой способ сказать это вслух.
<a href="https://github.com/nexu-io/open-design/graphs/contributors"> <a href="https://github.com/nexu-io/open-design/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-05-04" alt="Contributors Open Design" /> <img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-09-30" alt="Contributors Open Design" />
</a> </a>
Если вы только что отправили свой первый PR — добро пожаловать. Метка [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) — хорошая точка входа. Если вы только что отправили свой первый PR — добро пожаловать. Метка [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) — хорошая точка входа.
@@ -721,9 +721,9 @@ SVG выше ежедневно пересобирается workflow [`.github/
<a href="https://star-history.com/#nexu-io/open-design&Date"> <a href="https://star-history.com/#nexu-io/open-design&Date">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-09-30" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
<img alt="История звёзд Open Design" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <img alt="История звёзд Open Design" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
</picture> </picture>
</a> </a>
+4 -4
View File
@@ -704,7 +704,7 @@ OD не зупиняється на коді. Та сама поверхня ч
Дякуємо всім, хто допоміг просувати Open Design — через код, документацію, зворотний зв'язок, нові навички, нові системи дизайну або навіть гостре питання. Кожен реальний внесок рахується, а стіна нижче — найпростіший спосіб сказати це вголос. Дякуємо всім, хто допоміг просувати Open Design — через код, документацію, зворотний зв'язок, нові навички, нові системи дизайну або навіть гостре питання. Кожен реальний внесок рахується, а стіна нижче — найпростіший спосіб сказати це вголос.
<a href="https://github.com/nexu-io/open-design/graphs/contributors"> <a href="https://github.com/nexu-io/open-design/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-05-04" alt="Контриб'ютори Open Design" /> <img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-09-30" alt="Контриб'ютори Open Design" />
</a> </a>
Якщо ви злили свій перший PR — ласкаво просимо. Мітка [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) — це точка входу. Якщо ви злили свій перший PR — ласкаво просимо. Мітка [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) — це точка входу.
@@ -721,9 +721,9 @@ SVG вище перегенерується щодня [`.github/workflows/metri
<a href="https://star-history.com/#nexu-io/open-design&Date"> <a href="https://star-history.com/#nexu-io/open-design&Date">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-09-30" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
<img alt="Історія зірок Open Design" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <img alt="Історія зірок Open Design" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
</picture> </picture>
</a> </a>
+4 -4
View File
@@ -694,7 +694,7 @@ Daemon 启动时从 `PATH` 自动检测,无需配置。流式分发逻辑在 [
感谢每一位让 Open Design 变得更好的朋友 —— 无论是写代码、修文档、提 issue、加 skill 还是加 design system,每一次真实贡献都会被记住。下面这面墙是最直观的「Thank you」。 感谢每一位让 Open Design 变得更好的朋友 —— 无论是写代码、修文档、提 issue、加 skill 还是加 design system,每一次真实贡献都会被记住。下面这面墙是最直观的「Thank you」。
<a href="https://github.com/nexu-io/open-design/graphs/contributors"> <a href="https://github.com/nexu-io/open-design/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-05-04" alt="Open Design 贡献者" /> <img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-09-30" alt="Open Design 贡献者" />
</a> </a>
第一次提 PR?欢迎从 [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) 标签起步。 第一次提 PR?欢迎从 [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) 标签起步。
@@ -711,9 +711,9 @@ Daemon 启动时从 `PATH` 自动检测,无需配置。流式分发逻辑在 [
<a href="https://star-history.com/#nexu-io/open-design&Date"> <a href="https://star-history.com/#nexu-io/open-design&Date">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-09-30" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
<img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
</picture> </picture>
</a> </a>
+4 -4
View File
@@ -620,7 +620,7 @@ Daemon 啟動時從 `PATH` 自動檢測,無需配置。流式分發邏輯在 [
感謝每一位讓 Open Design 變得更好的朋友 —— 無論是寫程式碼、修文檔、提 issue、加 skill 還是加 design system,每一次真實貢獻都會被記住。下面這面牆是最直觀的「Thank you」。 感謝每一位讓 Open Design 變得更好的朋友 —— 無論是寫程式碼、修文檔、提 issue、加 skill 還是加 design system,每一次真實貢獻都會被記住。下面這面牆是最直觀的「Thank you」。
<a href="https://github.com/nexu-io/open-design/graphs/contributors"> <a href="https://github.com/nexu-io/open-design/graphs/contributors">
<img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-05-04" alt="Open Design 貢獻者" /> <img src="https://contrib.rocks/image?repo=nexu-io/open-design&cache_bust=2026-09-30" alt="Open Design 貢獻者" />
</a> </a>
第一次提 PR?歡迎從 [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) 標籤起步。 第一次提 PR?歡迎從 [`good-first-issue`](https://github.com/nexu-io/open-design/labels/good-first-issue) 標籤起步。
@@ -637,9 +637,9 @@ Daemon 啟動時從 `PATH` 自動檢測,無需配置。流式分發邏輯在 [
<a href="https://star-history.com/#nexu-io/open-design&Date"> <a href="https://star-history.com/#nexu-io/open-design&Date">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&theme=dark&cache_bust=2026-09-30" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
<img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-05-04" /> <img alt="Open Design star history" src="https://api.star-history.com/svg?repos=nexu-io/open-design&type=Date&cache_bust=2026-09-30" />
</picture> </picture>
</a> </a>