Skip to content

chore: license under MIT #42

chore: license under MIT

chore: license under MIT #42

Workflow file for this run

# Run tests and on main push create prerelease with npm package.
# When you "Set as latest release" (uncheck prerelease), the npm-release workflow publishes to npm.
name: CI (test, prerelease)
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
run: npm ci
- name: Run tests with coverage
run: npm run test:coverage
- name: Conformance corpus (JS)
run: npm run test:conformance
# The third target. The JS build and the Rust crate were both checked against the corpus; the
# Tish source itself was not, so "one source, three targets" had a leg missing.
- name: Conformance corpus (Tish)
run: npm run test:conformance:tish
- name: Examples
run: npm run examples
- name: Build
run: npm run build
# The player is the host half: Web Audio, transport, host defaults/clamps. It builds against
# the language package IN THIS REPO (a file: link), so a grammar change that breaks playback
# fails here rather than after publish.
- name: Player (@spacedevin/deck-player)
run: npm test -w @spacedevin/deck-player
# The Rust crate is emitted from the SAME src/index.tish as dist/deck.js, which is what stops the
# JS host and tish-gba's build-time bake from drifting. Nothing verified that: a change that broke
# the rust-lib emit, or that made the two targets parse differently, would have gone unnoticed
# until release — or until a consumer hit it. `cargo test` here runs the same conformance corpus
# the JS build is checked against, so both targets are held to one contract on every PR.
rust_crate:
name: Rust crate (deckfile)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
run: npm ci
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: Swatinem/rust-cache@v2
with:
workspaces: crate
- name: Emit the crate
run: |
export PATH="$PWD/node_modules/.bin:$PATH"
npm run build:rust
- name: Conformance corpus (from Rust)
working-directory: crate
run: cargo test
# A crate that cannot be packaged cannot be released; catch that on the PR, not at publish.
- name: Packaging check
working-directory: crate
run: cargo publish --dry-run --allow-dirty
release_check:
name: Release check (semantic-release dry-run)
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies (incl. semantic-release)
run: npm ci
- name: Set repository URL
run: |
node -e "
const fs = require('fs');
const p = require('./package.json');
p.repository = { type: 'git', url: process.env.REPO_URL };
fs.writeFileSync('./package.json', JSON.stringify(p, null, 2));
"
env:
REPO_URL: ${{ format('{0}/{1}.git', github.server_url, github.repository) }}
- name: Require incremental release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npx semantic-release --dry-run --extends ./.releaserc.json 2>&1 | tee release-check.log
if grep -q "There are no relevant changes, so no new version is released." release-check.log; then
echo "No incremental release would be triggered. Push commits that trigger a release (feat/fix/perf/BREAKING CHANGE per conventional commits)."
exit 1
fi
echo "Release would be triggered - check passed."
release:
name: Release (prerelease branch + GitHub API)
needs: [test, rust_crate, release_check]
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
run: npm ci
- name: Set repository URL
run: |
node -e "
const fs = require('fs');
const p = require('./package.json');
p.repository = { type: 'git', url: process.env.REPO_URL };
fs.writeFileSync('./package.json', JSON.stringify(p, null, 2));
"
env:
REPO_URL: ${{ format('{0}/{1}.git', github.server_url, github.repository) }}
- name: Get next version (semantic-release dry-run)
id: next_version
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
npx semantic-release --dry-run --extends ./.releaserc.json 2>&1 | tee release.log
VERSION=$(sed -n 's/.*Published release \([0-9.]*\).*/\1/p' release.log | head -1)
if [ -z "$VERSION" ]; then
echo "Could not determine next version from semantic-release output"
exit 1
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Next version: $VERSION"
- name: Set package version
run: |
node -e "
const fs = require('fs');
const p = require('./package.json');
p.version = process.env.VERSION;
fs.writeFileSync('./package.json', JSON.stringify(p, null, 2));
"
env:
VERSION: ${{ steps.next_version.outputs.version }}
- name: Create npm package tarball (@spacedevin/deck)
run: |
npm pack
mv spacedevin-deck-*.tgz spacedevin-deck-npm-package.tgz
# The player releases in lockstep: same tag, same version. Its dependency on the language
# package is `file:../..` so the workspace links locally and CI tests the source in this repo —
# that has to become the real published version before packing, or the tarball is uninstallable.
#
# Caret, not an exact pin. `tish build` inlines the parser into dist/deck-player.js, so the
# dependency only matters to a consumer compiling from src/index.tish via the `tish` export
# condition — and for them a compatible minor is fine. An exact pin would just force npm to
# install a second copy alongside a consumer's own @spacedevin/deck.
- name: Set player version and pin its dependency
run: |
node -e "
const fs = require('fs');
const p = './packages/player/package.json';
const j = JSON.parse(fs.readFileSync(p));
j.version = process.env.VERSION;
j.dependencies['@spacedevin/deck'] = '^' + process.env.VERSION;
fs.writeFileSync(p, JSON.stringify(j, null, 2) + '\n');
"
env:
VERSION: ${{ steps.next_version.outputs.version }}
- name: Create npm package tarball (@spacedevin/deck-player)
run: |
npm pack -w @spacedevin/deck-player
mv spacedevin-deck-player-*.tgz spacedevin-deck-player-npm-package.tgz
- name: Create or update release branch and push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
BRANCH="release/v${{ steps.next_version.outputs.version }}"
git checkout -B "$BRANCH"
git push origin "$BRANCH" --force
- name: Generate release notes from commits
id: changelog
run: |
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -n "$LAST_TAG" ]; then
CHANGELOG=$(git log "$LAST_TAG..HEAD" --pretty=format:"- %s (%h)" --no-merges 2>/dev/null || echo "")
else
CHANGELOG=$(git log -30 --pretty=format:"- %s (%h)" --no-merges 2>/dev/null || echo "")
fi
[ -z "$CHANGELOG" ] && CHANGELOG="- No commits to list"
{
echo "## Changes"
echo ""
echo "$CHANGELOG"
echo ""
} > release-body.md
- name: Create or update GitHub prerelease via API
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.next_version.outputs.version }}
run: |
TAG="v${VERSION}"
BRANCH="release/v${VERSION}"
BODY=$(cat release-body.md)
RESP=$(curl -s -w "\n%{http_code}" -X POST \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/${{ github.repository }}/releases" \
-d "{\"tag_name\":\"$TAG\",\"target_commitish\":\"$BRANCH\",\"name\":\"$TAG\",\"body\":$(echo "$BODY" | jq -Rs .),\"prerelease\":true}")
HTTP_CODE=$(echo "$RESP" | tail -n1)
BODY_RESP=$(echo "$RESP" | sed '$d')
if [ "$HTTP_CODE" = "201" ]; then
echo "Created prerelease $TAG"
RELEASE_ID=$(echo "$BODY_RESP" | jq -r .id)
elif [ "$HTTP_CODE" = "422" ]; then
RELEASE_ID=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
"https://api.github.com/repos/${{ github.repository }}/releases/tags/$TAG" | jq -r .id)
if [ "$RELEASE_ID" != "null" ] && [ -n "$RELEASE_ID" ]; then
curl -s -X PATCH \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/${{ github.repository }}/releases/$RELEASE_ID" \
-d "{\"target_commitish\":\"$BRANCH\",\"body\":$(echo "$BODY" | jq -Rs .),\"prerelease\":true}"
echo "Updated existing prerelease $TAG"
else
echo "Release creation failed (422) and could not find release by tag"
exit 1
fi
else
echo "Unexpected response: $HTTP_CODE"
echo "$BODY_RESP"
exit 1
fi
echo "release_id=$RELEASE_ID" >> $GITHUB_OUTPUT
- name: Upload npm package to prerelease
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
RELEASE_ID="${{ steps.create_release.outputs.release_id }}"
REPO="${{ github.repository }}"
UPLOAD_URL="https://uploads.github.com/repos/$REPO/releases/$RELEASE_ID/assets"
curl -s -X POST \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
-H "Content-Type: application/octet-stream" \
--data-binary @spacedevin-deck-npm-package.tgz \
"${UPLOAD_URL}?name=spacedevin-deck-npm-package.tgz&label=npm%20package%20(@spacedevin/deck)"
curl -s -X POST \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
-H "Content-Type: application/octet-stream" \
--data-binary @spacedevin-deck-player-npm-package.tgz \
"${UPLOAD_URL}?name=spacedevin-deck-player-npm-package.tgz&label=npm%20package%20(@spacedevin/deck-player)"