Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 45 additions & 16 deletions .github/workflows/csharp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -312,20 +312,23 @@ jobs:
dotnet build --configuration Release
dotnet pack --no-build --configuration Release --output ./artifacts

- name: Resolve NuGet package id
- name: Resolve NuGet package ids
id: package
if: >-
steps.version.outputs.version_committed == 'true' ||
steps.version.outputs.already_released == 'true' ||
(steps.check_release.outputs.should_release == 'true' && steps.check_release.outputs.skip_bump == 'true')
run: |
PACKAGE_ID=$(sed -n 's:.*<PackageId>\(.*\)</PackageId>.*:\1:p' Foundation.Data.Doublets.Cli/Foundation.Data.Doublets.Cli.csproj | head -n 1)
if [ -z "$PACKAGE_ID" ]; then
PACKAGE_ID=clink
CLI_ID=$(sed -n 's:.*<PackageId>\(.*\)</PackageId>.*:\1:p' Foundation.Data.Doublets.Cli/Foundation.Data.Doublets.Cli.csproj | head -n 1)
if [ -z "$CLI_ID" ]; then
CLI_ID=clink
fi
PACKAGE_ID_LOWER=$(echo "$PACKAGE_ID" | tr '[:upper:]' '[:lower:]')
echo "id=$PACKAGE_ID" >> "$GITHUB_OUTPUT"
echo "flat_container_id=$PACKAGE_ID_LOWER" >> "$GITHUB_OUTPUT"
LIB_ID=$(sed -n 's:.*<PackageId>\(.*\)</PackageId>.*:\1:p' Foundation.Data.Doublets.Cli.Library/Foundation.Data.Doublets.Cli.Library.csproj | head -n 1)
if [ -z "$LIB_ID" ]; then
LIB_ID=Foundation.Data.Doublets.Cli
fi
echo "id=$CLI_ID" >> "$GITHUB_OUTPUT"
echo "library_id=$LIB_ID" >> "$GITHUB_OUTPUT"

- name: Validate NuGet API key
# Upfront validation surfaces an expired/invalid NUGET_API_KEY before
Expand Down Expand Up @@ -360,7 +363,7 @@ jobs:
echo "published=false" >> "$GITHUB_OUTPUT"
fi

- name: Verify package on NuGet
- name: Verify CLI package on NuGet
if: >-
steps.nuget_publish.outputs.published == 'true' && (
steps.version.outputs.version_committed == 'true' ||
Expand All @@ -371,6 +374,17 @@ jobs:
--package-id "${{ steps.package.outputs.id }}" \
--release-version "${{ steps.release_version.outputs.version }}"

- name: Verify library package on NuGet
if: >-
steps.nuget_publish.outputs.published == 'true' && (
steps.version.outputs.version_committed == 'true' ||
steps.version.outputs.already_released == 'true' ||
(steps.check_release.outputs.should_release == 'true' && steps.check_release.outputs.skip_bump == 'true'))
run: |
node scripts/wait-for-nuget.mjs \
--package-id "${{ steps.package.outputs.library_id }}" \
--release-version "${{ steps.release_version.outputs.version }}"

- name: Create GitHub Release
if: >-
steps.version.outputs.version_committed == 'true' ||
Expand All @@ -386,6 +400,7 @@ jobs:
--tag-prefix "csharp-v" \
--language "C#" \
--package-id "${{ steps.package.outputs.id }}" \
--package-id "${{ steps.package.outputs.library_id }}" \
--changelog-path "csharp/CHANGELOG.md" \
--assets-glob "csharp/artifacts/*.nupkg"

Expand Down Expand Up @@ -432,19 +447,22 @@ jobs:
dotnet build --configuration Release
dotnet pack --no-build --configuration Release --output ./artifacts

- name: Resolve NuGet package id
- name: Resolve NuGet package ids
id: package
if: >-
steps.version.outputs.version_committed == 'true' ||
steps.version.outputs.already_released == 'true'
run: |
PACKAGE_ID=$(sed -n 's:.*<PackageId>\(.*\)</PackageId>.*:\1:p' Foundation.Data.Doublets.Cli/Foundation.Data.Doublets.Cli.csproj | head -n 1)
if [ -z "$PACKAGE_ID" ]; then
PACKAGE_ID=clink
CLI_ID=$(sed -n 's:.*<PackageId>\(.*\)</PackageId>.*:\1:p' Foundation.Data.Doublets.Cli/Foundation.Data.Doublets.Cli.csproj | head -n 1)
if [ -z "$CLI_ID" ]; then
CLI_ID=clink
fi
PACKAGE_ID_LOWER=$(echo "$PACKAGE_ID" | tr '[:upper:]' '[:lower:]')
echo "id=$PACKAGE_ID" >> "$GITHUB_OUTPUT"
echo "flat_container_id=$PACKAGE_ID_LOWER" >> "$GITHUB_OUTPUT"
LIB_ID=$(sed -n 's:.*<PackageId>\(.*\)</PackageId>.*:\1:p' Foundation.Data.Doublets.Cli.Library/Foundation.Data.Doublets.Cli.Library.csproj | head -n 1)
if [ -z "$LIB_ID" ]; then
LIB_ID=Foundation.Data.Doublets.Cli
fi
echo "id=$CLI_ID" >> "$GITHUB_OUTPUT"
echo "library_id=$LIB_ID" >> "$GITHUB_OUTPUT"

- name: Publish to NuGet
id: nuget_publish
Expand All @@ -462,7 +480,7 @@ jobs:
echo "published=false" >> "$GITHUB_OUTPUT"
fi

- name: Verify package on NuGet
- name: Verify CLI package on NuGet
if: >-
steps.nuget_publish.outputs.published == 'true' && (
steps.version.outputs.version_committed == 'true' ||
Expand All @@ -472,6 +490,16 @@ jobs:
--package-id "${{ steps.package.outputs.id }}" \
--release-version "${{ steps.version.outputs.new_version }}"

- name: Verify library package on NuGet
if: >-
steps.nuget_publish.outputs.published == 'true' && (
steps.version.outputs.version_committed == 'true' ||
steps.version.outputs.already_released == 'true')
run: |
node scripts/wait-for-nuget.mjs \
--package-id "${{ steps.package.outputs.library_id }}" \
--release-version "${{ steps.version.outputs.new_version }}"

- name: Create GitHub Release
if: >-
steps.version.outputs.version_committed == 'true' ||
Expand All @@ -486,5 +514,6 @@ jobs:
--tag-prefix "csharp-v" \
--language "C#" \
--package-id "${{ steps.package.outputs.id }}" \
--package-id "${{ steps.package.outputs.library_id }}" \
--changelog-path "csharp/CHANGELOG.md" \
--assets-glob "csharp/artifacts/*.nupkg"
159 changes: 159 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
name: Docs

# Build and deploy a unified API reference site that hosts both the C#
# (`Foundation.Data.Doublets.Cli`) and Rust (`link-cli`) library docs.
#
# GitHub Pages allows only one deployment per repository, so this workflow
# combines DocFX-generated C# docs (under `/csharp/`) and `cargo doc`-generated
# Rust docs (under `/rust/`) into a single site that also includes a small
# landing page linking out to both. The deploy job runs on pushes to `main`
# and on manual dispatch, mirroring the AI driven development pipeline
# templates' approach (see docs/case-studies/issue-92/templates).

on:
push:
branches: [main]
paths:
- 'csharp/Foundation.Data.Doublets.Cli.Library/**'
- 'csharp/docs/**'
- 'csharp/docfx.json'
- 'rust/src/**'
- 'rust/Cargo.toml'
- '.github/workflows/docs.yml'
pull_request:
branches: [main]
paths:
- 'csharp/Foundation.Data.Doublets.Cli.Library/**'
- 'csharp/docs/**'
- 'csharp/docfx.json'
- 'rust/src/**'
- 'rust/Cargo.toml'
- '.github/workflows/docs.yml'
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: docs-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_NOLOGO: true
CARGO_TERM_COLOR: always

jobs:
build:
name: Build documentation
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v6

- name: Setup .NET
uses: actions/setup-dotnet@v5
with:
dotnet-version: '8.0.x'

- name: Setup Rust
uses: dtolnay/rust-toolchain@stable

- name: Cache cargo registry
uses: actions/cache@v5
with:
path: |
~/.cargo/registry
~/.cargo/git
rust/target
key: ${{ runner.os }}-cargo-docs-${{ hashFiles('rust/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-docs-

- name: Install DocFX
run: dotnet tool update -g docfx

- name: Restore C# dependencies
working-directory: csharp
run: dotnet restore

- name: Build C# documentation
working-directory: csharp
run: docfx docfx.json -o _site

- name: Build Rust documentation
run: cargo doc --manifest-path rust/Cargo.toml --no-deps --all-features

- name: Assemble unified site
run: |
set -euo pipefail
mkdir -p _site/csharp _site/rust
cp -R csharp/_site/. _site/csharp/
cp -R rust/target/doc/. _site/rust/
# Landing page that links into both sub-sites.
cat > _site/index.html <<'HTML'
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>link-cli API documentation</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style>
body { font-family: system-ui, sans-serif; max-width: 40rem; margin: 4rem auto; padding: 0 1rem; line-height: 1.5; }
h1 { margin-bottom: 0.25rem; }
ul { list-style: none; padding: 0; }
li { margin: 0.5rem 0; }
a { text-decoration: none; color: #0366d6; }
a:hover { text-decoration: underline; }
code { background: #f3f3f3; padding: 0.1rem 0.3rem; border-radius: 0.2rem; }
</style>
</head>
<body>
<h1>link-cli API documentation</h1>
<p>Generated reference for the C# and Rust library packages that
ship alongside the <code>clink</code> CLI.</p>
<ul>
<li><a href="csharp/">C# – <code>Foundation.Data.Doublets.Cli</code> (DocFX)</a></li>
<li><a href="rust/link_cli/">Rust – <code>link-cli</code> (rustdoc)</a></li>
</ul>
<p>Source: <a href="https://github.com/link-foundation/link-cli">github.com/link-foundation/link-cli</a></p>
</body>
</html>
HTML

- name: List unified site (debug)
run: |
echo "::group::_site tree"
find _site -maxdepth 3 -print
echo "::endgroup::"

- name: Configure GitHub Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
uses: actions/configure-pages@v6

- name: Upload GitHub Pages artifact
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
uses: actions/upload-pages-artifact@v5
with:
path: _site

deploy:
name: Deploy to GitHub Pages
if: github.event_name == 'push' && github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch'
needs: build
runs-on: ubuntu-latest
timeout-minutes: 10
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy GitHub Pages
id: deployment
uses: actions/deploy-pages@v5

- name: Print resolved deployment URL (debug)
run: |
echo "Pages deployed to: ${{ steps.deployment.outputs.page_url }}"
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,9 @@ ci-logs/
gh-data/
experiments/
.playwright-mcp/

# Generated API documentation
csharp/_site/
csharp/docs/api/*.yml
csharp/docs/api/*.manifest
_site/
58 changes: 45 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,26 @@
[![C# CI/CD Pipeline](https://github.com/link-foundation/link-cli/actions/workflows/csharp.yml/badge.svg)](https://github.com/link-foundation/link-cli/actions/workflows/csharp.yml)
[![Rust CI/CD Pipeline](https://github.com/link-foundation/link-cli/actions/workflows/rust.yml/badge.svg)](https://github.com/link-foundation/link-cli/actions/workflows/rust.yml)
[![WebAssembly CI](https://github.com/link-foundation/link-cli/actions/workflows/wasm.yml/badge.svg)](https://github.com/link-foundation/link-cli/actions/workflows/wasm.yml)
[![NuGet](https://img.shields.io/nuget/v/clink?logo=nuget&label=NuGet)](https://www.nuget.org/packages/clink)
[![NuGet (clink)](https://img.shields.io/nuget/v/clink?logo=nuget&label=clink)](https://www.nuget.org/packages/clink)
[![NuGet (library)](https://img.shields.io/nuget/v/Foundation.Data.Doublets.Cli?logo=nuget&label=Foundation.Data.Doublets.Cli)](https://www.nuget.org/packages/Foundation.Data.Doublets.Cli)
[![Crates.io](https://img.shields.io/crates/v/link-cli?logo=rust&label=Crates.io)](https://crates.io/crates/link-cli)
[![Docs.rs](https://docs.rs/link-cli/badge.svg)](https://docs.rs/link-cli)
[![C# Release](https://img.shields.io/github/v/release/link-foundation/link-cli?filter=csharp-v*&label=C%23%20release)](https://github.com/link-foundation/link-cli/releases?q=C%23&expanded=true)
[![Rust Release](https://img.shields.io/github/v/release/link-foundation/link-cli?filter=rust-v*&label=Rust%20release)](https://github.com/link-foundation/link-cli/releases?q=Rust&expanded=true)

`clink` (`CLInk` `cLINK`), a CLI tool to manipulate links using single substitution operation.

It is based on [associative theory](https://habr.com/ru/articles/895896) (also in [ru](https://habr.com/ru/articles/804617)) and [Links Notation](https://github.com/linksplatform/Protocols.Lino) (also in [ru](https://github.com/linksplatform/Protocols.Lino/blob/main/README.ru.md))

It includes a production C# CLI package, a Rust CLI/library package, and a
Rust-powered WebAssembly browser workbench built on [links data store](https://github.com/linksplatform?view_as=public)
It includes a production C# CLI/library pair on NuGet, a Rust CLI/library
crate on Crates.io, and a Rust-powered WebAssembly browser workbench built on
[links data store](https://github.com/linksplatform?view_as=public)
concepts (see also in [ru](https://github.com/linksplatform/.github/blob/main/profile/README.ru.md)).
Both ecosystems ship a runnable CLI and a reusable public library with full
auto-generated API documentation (DocFX for C#, `cargo doc`/docs.rs for Rust)
so external projects can embed the parser, query processors, decorators,
named/pinned types, persistent transformation triggers, and LiNo I/O without
re-implementing any of the internals.

## WebAssembly Browser Workbench

Expand All @@ -31,29 +39,53 @@ package built from `doublets-rs`.
- [docs/REQUIREMENTS.md](docs/REQUIREMENTS.md): implemented and planned requirements collected from issues and PR comments.
- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md): repository layout, major components, dependencies, storage files, and CI.
- [docs/HOW-IT-WORKS.md](docs/HOW-IT-WORKS.md): deeper explanation of query processing, references, import/export, triggers, and the WebAssembly workbench.
- [docs/case-studies/issue-71/README.md](docs/case-studies/issue-71/README.md): evidence and analysis behind this documentation refresh.
- [docs/case-studies/issue-71/README.md](docs/case-studies/issue-71/README.md): evidence and analysis behind the original documentation refresh.
- [docs/case-studies/issue-92/README.md](docs/case-studies/issue-92/README.md): evidence and analysis behind the dual CLI + library packaging and unified API documentation site.

### API references

- C# library: package page at <https://www.nuget.org/packages/Foundation.Data.Doublets.Cli> and the DocFX-generated reference on [GitHub Pages](https://link-foundation.github.io/link-cli/csharp/).
- Rust library: <https://docs.rs/link-cli> (also mirrored on [GitHub Pages](https://link-foundation.github.io/link-cli/rust/link_cli/)).
- Combined landing page: <https://link-foundation.github.io/link-cli/>.

## Installation

Language package documentation:

- [C# NuGet tool](csharp/README.md)
- [Rust crate](rust/README.md)
- [C# NuGet tool and library](csharp/README.md)
- [Rust crate (CLI + library)](rust/README.md)

Each language ships **both** a CLI binary and a public library, so external
projects can either run the tool directly or pull in the parser, query
processors, decorators, and LiNo I/O via a package reference.

This CLI tool can be installed globally as `clink` using single command (that will work if you have [.NET](https://dotnet.microsoft.com/en-us/download) installed):
### C# (.NET)

```bash
# CLI: install the `clink` command globally.
dotnet tool install --global clink

# Library: embed the parser, processors, and decorators in another .NET project.
dotnet add package Foundation.Data.Doublets.Cli
```

<img width="811" alt="Screenshot 2025-05-16 at 5 48 06 AM" src="https://github.com/user-attachments/assets/615df4ce-658e-4bab-a483-96fae200f106" />

The NuGet tool is the C# implementation and exposes the complete production
command surface, including persistent transformation triggers. The Rust
implementation under `rust/` mirrors the core query engine, named references,
LiNo import/export, structure formatting, and the WebAssembly workbench API.
Persistent transformation trigger CLI options currently exist only in the C#
tool.
### Rust

```bash
# CLI: build and install the `clink` binary from crates.io.
cargo install link-cli

# Library: pull `link_cli` into your own Cargo project.
cargo add link-cli
```

The NuGet CLI tool is the C# implementation and exposes the complete production
command surface, including persistent transformation triggers. The Rust crate
mirrors the core query engine, named references, LiNo import/export, structure
formatting, and the WebAssembly workbench API. Persistent transformation
trigger CLI options currently exist only in the C# tool.

This tool provides all CRUD operations for links using single [substitution operation](https://en.wikipedia.org/wiki/Markov_algorithm) ([ru](https://ru.wikipedia.org/wiki/Нормальный_алгоритм)) which is turing complete.

Expand Down
Loading
Loading