name: Publish inspection and optional packages on: workflow_dispatch: jobs: publish: runs-on: ubuntu-latest permissions: contents: read packages: write env: NUGET_AUTH_TOKEN: ${{ secrets.SHRINKSDK_PACKAGE_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.SHRINKSDK_PACKAGE_TOKEN }} DOTNET_SYSTEM_GLOBALIZATION_INVARIANT: '1' DOTNET_CLI_TELEMETRY_OPTOUT: '1' LD_LIBRARY_PATH: /opt/dotnet-libs/usr/lib/x86_64-linux-gnu SSL_CERT_FILE: /opt/ca-certificates.crt steps: - name: Fetch exact workspace and required submodules env: RELEASE_SHA: ${{ gitea.sha }} shell: bash run: | set -euo pipefail node --input-type=module <<'NODE' import { mkdir, writeFile } from 'node:fs/promises'; import { execFileSync } from 'node:child_process'; const base = 'https://git.crash.work'; const sha = process.env.RELEASE_SHA; if (!/^[a-f0-9]{40}$/.test(sha)) throw new Error('An exact commit is required'); async function fetchSource(repo, revision, destination) { const response = await fetch(`${base}/ShrinkSDK/${repo}/archive/${revision}.tar.gz`); if (!response.ok) throw new Error(`Cannot fetch ${repo}: ${response.status}`); await writeFile('source.tar.gz', new Uint8Array(await response.arrayBuffer())); await mkdir(destination, { recursive: true }); execFileSync('tar', ['-xzf', 'source.tar.gz', '--strip-components=1', '-C', destination]); } await fetchSource('Workspace', sha, 'release'); const response = await fetch(`${base}/api/v1/repos/ShrinkSDK/Workspace/git/trees/${sha}?recursive=true`); if (!response.ok) throw new Error(`Cannot resolve submodules: ${response.status}`); const tree = await response.json(); for (const repo of ['ShrinkShared.CodeGen', 'ShrinkNetwork']) { const path = `Assets/Modules/${repo}`; const entry = tree.tree.find(item => item.path === path && item.type === 'commit'); if (!entry) throw new Error(`Missing pinned submodule: ${path}`); await fetchSource(repo, entry.sha, `release/${path}`); } NODE - name: Install .NET 8 SDK shell: bash run: | set -euo pipefail node --input-type=module <<'NODE' import { writeFile } from 'node:fs/promises'; import { rootCertificates } from 'node:tls'; await writeFile('/opt/ca-certificates.crt', rootCertificates.join('\n')); const metadataResponse = await fetch('https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/8.0/releases.json'); if (!metadataResponse.ok) throw new Error(`Release metadata download failed: ${metadataResponse.status}`); const metadata = await metadataResponse.json(); const sdkVersion = metadata['latest-sdk']; const release = metadata.releases.find(item => item.sdk?.version === sdkVersion); const file = release?.sdk?.files?.find(item => item.rid === 'linux-x64' && item.name.endsWith('.tar.gz')); if (!file) throw new Error(`Linux x64 SDK archive not found for ${sdkVersion}`); const archiveResponse = await fetch(file.url); if (!archiveResponse.ok) throw new Error(`SDK download failed: ${archiveResponse.status}`); await writeFile('/tmp/dotnet-sdk.tar.gz', new Uint8Array(await archiveResponse.arrayBuffer())); const poolUrl = 'https://deb.debian.org/debian-security/pool/updates/main/o/openssl/'; const poolResponse = await fetch(poolUrl); if (!poolResponse.ok) throw new Error(`OpenSSL package index download failed: ${poolResponse.status}`); const poolIndex = await poolResponse.text(); const packages = [...poolIndex.matchAll(/href="(libssl3_[^"]+_amd64\.deb)"/g)].map(match => match[1]).sort(); const packageName = packages.at(-1); if (!packageName) throw new Error('Debian libssl3 package was not found'); const packageResponse = await fetch(poolUrl + packageName); if (!packageResponse.ok) throw new Error(`OpenSSL package download failed: ${packageResponse.status}`); await writeFile('/tmp/libssl3.deb', new Uint8Array(await packageResponse.arrayBuffer())); NODE mkdir -p /opt/dotnet tar -xzf /tmp/dotnet-sdk.tar.gz -C /opt/dotnet mkdir -p /opt/dotnet-libs dpkg-deb -x /tmp/libssl3.deb /opt/dotnet-libs rm -f /tmp/dotnet-sdk.tar.gz rm -f /tmp/libssl3.deb /opt/dotnet/dotnet --info - name: Build supplemental NuGet packages shell: bash run: | set -euo pipefail export PATH="/opt/dotnet:$PATH" cd release projects=( Assets/ShrinkInspection/DotNet~/ShrinkSDK.Inspection.csproj Assets/Modules/ShrinkNetwork/Adapters~/MessagePack/ShrinkSDK.Network.MessagePack.csproj Tools/AgentSupport/Shrink.Inspect/Shrink.Inspect.csproj ) for project in "${projects[@]}"; do dotnet restore "$project" --configfile Assets/Modules/ShrinkNetwork/NuGet.Config dotnet pack "$project" -c Release --no-restore -o "$PWD/packages" done - name: Publish NuGet and UPM packages shell: bash run: | set -euo pipefail : "${NUGET_AUTH_TOKEN:?SHRINKSDK_PACKAGE_TOKEN is required}" export PATH="/opt/dotnet:$PATH" cd release dotnet nuget push 'packages/*.nupkg' --api-key "$NUGET_AUTH_TOKEN" --source https://git.crash.work/api/packages/ShrinkSDK/nuget/index.json --skip-duplicate npmrc="$HOME/.npmrc" trap 'rm -f "$npmrc"' EXIT printf '%s\n' \ 'registry=https://git.crash.work/api/packages/ShrinkSDK/npm/' \ '//git.crash.work/api/packages/ShrinkSDK/npm/:_authToken=${NODE_AUTH_TOKEN}' > "$npmrc" for package in Assets/ShrinkInspection Assets/Modules/ShrinkNetwork/Adapters~/MessagePack; do # Stage only Unity source and documentation; dotnet build output is not a UPM asset. stage="$PWD/upm-$(basename "$package")" mkdir "$stage" cp "$package/package.json" "$package/README.md" "$stage/" cp -r "$package/Runtime" "$stage/" if [[ -d "$package/Editor" ]]; then cp -r "$package/Editor" "$stage/"; fi find "$package" -maxdepth 1 -name '*.meta' -exec cp '{}' "$stage/" \; name="$(node -p "require('./$package/package.json').name")" version="$(node -p "require('./$package/package.json').version")" export PACKAGE_NAME="$name" PACKAGE_VERSION="$version" exists="$(node --input-type=module <<'NODE' const url = `https://git.crash.work/api/packages/ShrinkSDK/npm/${encodeURIComponent(process.env.PACKAGE_NAME)}`; const response = await fetch(url); if (response.status === 404) console.log('no'); else { if (!response.ok) throw new Error(`Registry lookup failed: ${response.status}`); const metadata = await response.json(); console.log(metadata.versions?.[process.env.PACKAGE_VERSION] ? 'yes' : 'no'); } NODE )" if [[ "$exists" = yes ]]; then echo "Already published: $name@$version"; continue; fi (cd "$stage" && npm publish --registry=https://git.crash.work/api/packages/ShrinkSDK/npm/) done