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
44 changes: 27 additions & 17 deletions .github/workflows/csharp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -207,14 +207,22 @@ jobs:
run: |
# Get current version from csproj
CURRENT_VERSION=$(grep -Po '(?<=<Version>)[^<]*' src/Lino.Objects.Codec/Lino.Objects.Codec.csproj)
PACKAGE_ID=$(grep -Po '(?<=<PackageId>)[^<]*' src/Lino.Objects.Codec/Lino.Objects.Codec.csproj)
PACKAGE_ID_LC=$(echo "$PACKAGE_ID" | tr '[:upper:]' '[:lower:]')
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT

# Check if tag exists
if git rev-parse "csharp-v$CURRENT_VERSION" >/dev/null 2>&1; then
echo "Tag csharp-v$CURRENT_VERSION already exists, skipping release"
echo "package_id=$PACKAGE_ID" >> $GITHUB_OUTPUT

# Decide based on the registry, not on the local git tag. NuGet's flat-container index
# returns 200 for an existing package id and 404 otherwise; the version-specific .nuspec
# endpoint pinpoints whether a particular version is published. See
# docs/case-studies/issue-25/README.md for why we no longer trust the tag.
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://api.nuget.org/v3-flatcontainer/${PACKAGE_ID_LC}/${CURRENT_VERSION}/${PACKAGE_ID_LC}.nuspec")
echo "NuGet HTTP status for ${PACKAGE_ID}@${CURRENT_VERSION}: ${STATUS}"
if [ "$STATUS" = "200" ]; then
echo "Version ${PACKAGE_ID}@${CURRENT_VERSION} already on NuGet, skipping publish"
echo "should_release=false" >> $GITHUB_OUTPUT
else
echo "New version detected: $CURRENT_VERSION"
echo "Version ${PACKAGE_ID}@${CURRENT_VERSION} is NOT on NuGet, will publish"
echo "should_release=true" >> $GITHUB_OUTPUT
fi

Expand All @@ -232,13 +240,14 @@ jobs:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
if [ -z "$NUGET_API_KEY" ]; then
echo "::warning::NUGET_API_KEY not set, skipping publish to NuGet"
else
dotnet nuget push artifacts/*.nupkg \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate
echo "::error::NUGET_API_KEY is not set; cannot publish to NuGet."
echo "Configure NUGET_API_KEY as a repository secret. See docs/case-studies/issue-25/README.md."
exit 1
fi
dotnet nuget push artifacts/*.nupkg \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate

- name: Create GitHub Release
if: steps.version_check.outputs.should_release == 'true'
Expand Down Expand Up @@ -304,13 +313,14 @@ jobs:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
if [ -z "$NUGET_API_KEY" ]; then
echo "::warning::NUGET_API_KEY not set, skipping publish to NuGet"
else
dotnet nuget push artifacts/*.nupkg \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate
echo "::error::NUGET_API_KEY is not set; cannot publish to NuGet."
echo "Configure NUGET_API_KEY as a repository secret. See docs/case-studies/issue-25/README.md."
exit 1
fi
dotnet nuget push artifacts/*.nupkg \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate

- name: Create GitHub Release
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
Expand Down
24 changes: 19 additions & 5 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,20 @@ jobs:
run: |
# Get current version from pyproject.toml
CURRENT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' pyproject.toml)
PACKAGE_NAME=$(grep -Po '(?<=^name = ")[^"]*' pyproject.toml | head -1)
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT

# Check if tag exists
if git rev-parse "python-v$CURRENT_VERSION" >/dev/null 2>&1; then
echo "Tag python-v$CURRENT_VERSION already exists, skipping release"
echo "package_name=$PACKAGE_NAME" >> $GITHUB_OUTPUT

# Decide based on the registry, not on the local git tag. PyPI returns 200 for an
# existing version's JSON metadata and 404 otherwise. See
# docs/case-studies/issue-25/README.md for why the tag is not a reliable proxy.
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://pypi.org/pypi/${PACKAGE_NAME}/${CURRENT_VERSION}/json")
echo "PyPI HTTP status for ${PACKAGE_NAME}@${CURRENT_VERSION}: ${STATUS}"
if [ "$STATUS" = "200" ]; then
echo "Version ${PACKAGE_NAME}@${CURRENT_VERSION} already on PyPI, skipping publish"
echo "should_release=false" >> $GITHUB_OUTPUT
else
echo "New version detected: $CURRENT_VERSION"
echo "Version ${PACKAGE_NAME}@${CURRENT_VERSION} is NOT on PyPI, will publish"
echo "should_release=true" >> $GITHUB_OUTPUT
fi

Expand All @@ -228,9 +234,14 @@ jobs:

- name: Publish to PyPI
if: steps.version_check.outputs.should_release == 'true'
# Uses PyPI's OIDC Trusted Publisher flow (id-token: write below). If you see "Trusted
# Publisher … not configured" in the logs, configure it for this repo + workflow on PyPI.
# See docs/case-studies/issue-25/README.md for the broader context.
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: python/dist/
verbose: true
skip-existing: true

- name: Create GitHub Release
if: steps.version_check.outputs.should_release == 'true'
Expand Down Expand Up @@ -306,9 +317,12 @@ jobs:

- name: Publish to PyPI
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
# See note above: relies on PyPI Trusted Publisher; see docs/case-studies/issue-25/README.md.
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: python/dist/
verbose: true
skip-existing: true

- name: Create GitHub Release
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
Expand Down
50 changes: 39 additions & 11 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -290,14 +290,21 @@ jobs:
run: |
# Get current version from Cargo.toml
CURRENT_VERSION=$(grep -Po '(?<=^version = ")[^"]*' Cargo.toml)
PACKAGE_NAME=$(grep -Po '(?<=^name = ")[^"]*' Cargo.toml | head -1)
echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT

# Check if tag exists
if git rev-parse "rust-v$CURRENT_VERSION" >/dev/null 2>&1; then
echo "Tag rust-v$CURRENT_VERSION already exists"
echo "package_name=$PACKAGE_NAME" >> $GITHUB_OUTPUT

# Decide based on the registry, not on the local git tag.
# A 200 from crates.io means the package@version is already published; otherwise we should
# publish. This makes the pipeline self-healing if a previous publish was silently skipped
# (see docs/case-studies/issue-25/README.md).
STATUS=$(curl -sS -o /dev/null -w '%{http_code}' "https://crates.io/api/v1/crates/${PACKAGE_NAME}/${CURRENT_VERSION}")
echo "crates.io HTTP status for ${PACKAGE_NAME}@${CURRENT_VERSION}: ${STATUS}"
if [ "$STATUS" = "200" ]; then
echo "Version ${PACKAGE_NAME}@${CURRENT_VERSION} already on crates.io, skipping publish"
echo "should_release=false" >> $GITHUB_OUTPUT
else
echo "New version detected: $CURRENT_VERSION"
echo "Version ${PACKAGE_NAME}@${CURRENT_VERSION} is NOT on crates.io, will publish"
echo "should_release=true" >> $GITHUB_OUTPUT
fi

Expand Down Expand Up @@ -329,10 +336,22 @@ jobs:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN || secrets.CARGO_TOKEN }}
run: |
if [ -z "$CARGO_REGISTRY_TOKEN" ]; then
echo "::warning::Neither CARGO_REGISTRY_TOKEN nor CARGO_TOKEN is set, skipping publish to crates.io"
else
cargo publish
echo "::error::Neither CARGO_REGISTRY_TOKEN nor CARGO_TOKEN is set; cannot publish to crates.io."
echo "Configure one of these as a repository secret. See docs/case-studies/issue-25/README.md."
exit 1
fi
# `cargo publish` exits non-zero on retry if the version already exists; we tolerate that
# because the registry probe in version_check already proved the version is missing, so a
# late "already exists" error means a parallel run won the race -- treat as success.
if ! OUT=$(cargo publish 2>&1); then
echo "$OUT"
if echo "$OUT" | grep -qE 'already (uploaded|exists)'; then
echo "::warning::Version was published by another run between probe and publish; treating as success."
exit 0
fi
exit 1
fi
echo "$OUT"

- name: Create GitHub Release
if: steps.version_check.outputs.should_release == 'true'
Expand Down Expand Up @@ -400,10 +419,19 @@ jobs:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN || secrets.CARGO_TOKEN }}
run: |
if [ -z "$CARGO_REGISTRY_TOKEN" ]; then
echo "::warning::Neither CARGO_REGISTRY_TOKEN nor CARGO_TOKEN is set, skipping publish to crates.io"
else
cargo publish
echo "::error::Neither CARGO_REGISTRY_TOKEN nor CARGO_TOKEN is set; cannot publish to crates.io."
echo "Configure one of these as a repository secret. See docs/case-studies/issue-25/README.md."
exit 1
fi
if ! OUT=$(cargo publish 2>&1); then
echo "$OUT"
if echo "$OUT" | grep -qE 'already (uploaded|exists)'; then
echo "::warning::Version is already on crates.io; treating manual re-run as success."
exit 0
fi
exit 1
fi
echo "$OUT"

- name: Create GitHub Release
if: steps.version.outputs.version_committed == 'true' || steps.version.outputs.already_released == 'true'
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,19 @@
[![Python CI](https://github.com/link-foundation/lino-objects-codec/actions/workflows/python.yml/badge.svg)](https://github.com/link-foundation/lino-objects-codec/actions/workflows/python.yml)
[![Rust CI](https://github.com/link-foundation/lino-objects-codec/actions/workflows/rust.yml/badge.svg)](https://github.com/link-foundation/lino-objects-codec/actions/workflows/rust.yml)
[![C# CI](https://github.com/link-foundation/lino-objects-codec/actions/workflows/csharp.yml/badge.svg)](https://github.com/link-foundation/lino-objects-codec/actions/workflows/csharp.yml)

### Package versions

[![npm](https://img.shields.io/npm/v/lino-objects-codec?label=npm&logo=npm)](https://www.npmjs.com/package/lino-objects-codec)
[![PyPI](https://img.shields.io/pypi/v/lino-objects-codec?label=PyPI&logo=pypi&logoColor=white)](https://pypi.org/project/lino-objects-codec/)
[![crates.io](https://img.shields.io/crates/v/lino-objects-codec?label=crates.io&logo=rust)](https://crates.io/crates/lino-objects-codec)
[![NuGet](https://img.shields.io/nuget/v/Lino.Objects.Codec?label=NuGet&logo=nuget)](https://www.nuget.org/packages/Lino.Objects.Codec)
[![Python Version](https://img.shields.io/pypi/pyversions/lino-objects-codec.svg)](https://pypi.org/project/lino-objects-codec/)

> A "no badge / not found" state on any of the registry badges above means the corresponding
> package has not been published yet for that version. The badges read live from the package
> registries, so they always reflect the latest published state.

Universal serialization library to encode/decode objects to/from Links Notation format. Available in **Python**, **JavaScript**, **Rust**, and **C#** with identical functionality and API design.

## 🌍 Multi-Language Support
Expand Down
5 changes: 5 additions & 0 deletions csharp/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# lino-objects-codec (C#)

[![C# CI](https://github.com/link-foundation/lino-objects-codec/actions/workflows/csharp.yml/badge.svg)](https://github.com/link-foundation/lino-objects-codec/actions/workflows/csharp.yml)
[![NuGet](https://img.shields.io/nuget/v/Lino.Objects.Codec?label=NuGet&logo=nuget)](https://www.nuget.org/packages/Lino.Objects.Codec)
[![NuGet downloads](https://img.shields.io/nuget/dt/Lino.Objects.Codec?label=downloads)](https://www.nuget.org/packages/Lino.Objects.Codec)
[![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](https://unlicense.org/)

A C# library for working with Links Notation format. This library provides universal serialization/deserialization for C# objects with circular reference support.

## Features
Expand Down
Loading
Loading