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
141 changes: 82 additions & 59 deletions .github/workflows/release-components.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,24 @@ jobs:
release-microcontroller:
name: Build and Upload microcontroller
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Create ZIP of 'microcontroller' directory
run: zip -r release-microcontroller-${{ github.ref_name }}.zip microcontroller
env:
TAG_NAME: ${{ github.ref_name }}
run: zip -r "release-microcontroller-${TAG_NAME}.zip" microcontroller

- name: Upload microcontroller ZIP to Release
uses: actions/upload-release-asset@v1
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./release-microcontroller-${{ github.ref_name }}.zip
asset_name: release-microcontroller-${{ github.ref_name }}.zip
asset_content_type: application/zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ github.ref_name }}
run: gh release upload "$TAG_NAME" "release-microcontroller-${TAG_NAME}.zip" --clobber

release-lang-cli-notebook:
name: Publish @bscript/lang, @bscript/cli, and @bscript/notebook
runs-on: ubuntu-latest
Expand All @@ -48,18 +48,25 @@ jobs:

- name: Determine npm dist-tag
id: npm_tag
env:
IS_PRERELEASE: ${{ github.event.release.prerelease }}
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"

if [ "${{ github.event.release.prerelease }}" = "true" ]; then
NPM_TAG=$(node -e "
const m = process.argv[1].match(/-([0-9A-Za-z-]+)/);
if [ "$IS_PRERELEASE" = "true" ]; then
NPM_TAG=$(node -e '
const version = process.argv[1];
const m = version.match(/^\d+\.\d+\.\d+-([0-9A-Za-z-]+)/);
if (!m) {
console.error('::error::GitHub pre-release requires a semver prerelease segment (e.g. 2.0.0-alpha.1)');
console.error("::error::GitHub pre-release requires a semver prerelease segment (e.g. 2.0.0-alpha.1)");
process.exit(1);
}
if (/^\d+$/.test(m[1])) {
console.error("::error::Prerelease identifier must not be numeric (" + m[1] + "); use e.g. 2.0.0-alpha.1");
process.exit(1);
}
process.stdout.write(m[1]);
" "$TAG_VERSION")
' "$TAG_VERSION")
echo "Pre-release: publishing to npm dist-tag '${NPM_TAG}' (version ${TAG_VERSION})"
else
if [[ "$TAG_VERSION" == *-* ]]; then
Expand All @@ -71,64 +78,80 @@ jobs:
fi

echo "tag=${NPM_TAG}" >> "$GITHUB_OUTPUT"
echo "version=${TAG_VERSION}" >> "$GITHUB_OUTPUT"

- name: Verify Version of @bscript/lang Matches Tag
- name: Verify Package Versions Match Tag
env:
TAG_VERSION: ${{ steps.npm_tag.outputs.version }}
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
PKG_VERSION=$(node -p "require('./lang/package.json').version")

if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "::error::Version Mismatch! Tag ($TAG_VERSION) != ./lang/package.json ($PKG_VERSION)"
exit 1
fi
echo "Version check of @bscript/lang passed: $TAG_VERSION"
node -e '
const fs = require("fs");
const tagVersion = process.env.TAG_VERSION;
const workspaces = ["lang", "notebook", "cli"];
const errors = [];

- name: Verify Version of @bscript/cli Matches Tag and @bscript/cli Installs Correct @bscript/lang Version
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
PKG_VERSION=$(node -p "require('./cli/package.json').version")
DEP_LANG_VERSION=$(node -p "require('./cli/package.json').dependencies['@bscript/lang']")

if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "::error::Version Mismatch! Tag ($TAG_VERSION) != ./cli/package.json ($PKG_VERSION)"
exit 1
fi
if [ "$TAG_VERSION" != "$DEP_LANG_VERSION" ]; then
echo "::error::Version Mismatch! @bscript/lang@($DEP_LANG_VERSION) in dependencies is incorrect."
exit 1
fi
echo "Version check of @bscript/cli passed: $TAG_VERSION"
for (const workspace of workspaces) {
const pkg = JSON.parse(fs.readFileSync(`./${workspace}/package.json`, "utf8"));

- name: Verify Version of @bscript/notebook Matches Tag and @bscript/notebook Installs Correct @bscript/lang Version
run: |
TAG_VERSION="${GITHUB_REF_NAME#v}"
PKG_VERSION=$(node -p "require('./notebook/package.json').version")
DEP_LANG_VERSION=$(node -p "require('./notebook/package.json').dependencies['@bscript/lang']")

if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
echo "::error::Version Mismatch! Tag ($TAG_VERSION) != ./notebook/package.json ($PKG_VERSION)"
exit 1
fi
if [ "$TAG_VERSION" != "$DEP_LANG_VERSION" ]; then
echo "::error::Version Mismatch! @bscript/lang@($DEP_LANG_VERSION) in dependencies is incorrect."
exit 1
fi
echo "Version check of @bscript/notebook passed: $TAG_VERSION"
if (pkg.version !== tagVersion) {
errors.push(`${pkg.name}: version ${pkg.version} does not match tag ${tagVersion}`);
}

for (const [dep, range] of Object.entries(pkg.dependencies ?? {})) {
if (dep.startsWith("@bscript/") && range !== tagVersion) {
errors.push(`${pkg.name}: dependency ${dep}@${range} must be pinned to ${tagVersion}`);
}
}
}

if (errors.length > 0) {
for (const error of errors) console.error(`::error::${error}`);
process.exit(1);
}

console.log(`Version check passed for ${workspaces.join(", ")}: ${tagVersion}`);
'

- name: Install Dependencies
run: npm ci

- name: Build Packages
run: npm run build --workspaces --if-present

# Published in dependency order (lang -> notebook -> cli) so that a freshly
# published package can always resolve the @bscript/* versions it depends on.
- name: Publish @bscript/lang to npm
working-directory: ./lang
run: npm publish --tag ${{ steps.npm_tag.outputs.tag }} || echo 'Failed or skipped publishing @bscript/lang'
env:
NPM_TAG: ${{ steps.npm_tag.outputs.tag }}
TAG_VERSION: ${{ steps.npm_tag.outputs.version }}
run: |
if npm view "@bscript/lang@${TAG_VERSION}" version > /dev/null 2>&1; then
echo "@bscript/lang@${TAG_VERSION} is already published; skipping."
exit 0
fi
npm publish --tag "$NPM_TAG"

- name: Publish @bscript/cli to npm
working-directory: ./cli
run: npm publish --tag ${{ steps.npm_tag.outputs.tag }} || echo 'Failed or skipped publishing @bscript/cli'

- name: Publish @bscript/notebook to npm
working-directory: ./notebook
run: npm publish --tag ${{ steps.npm_tag.outputs.tag }} || echo 'Failed or skipped publishing @bscript/notebook'
env:
NPM_TAG: ${{ steps.npm_tag.outputs.tag }}
TAG_VERSION: ${{ steps.npm_tag.outputs.version }}
run: |
if npm view "@bscript/notebook@${TAG_VERSION}" version > /dev/null 2>&1; then
echo "@bscript/notebook@${TAG_VERSION} is already published; skipping."
exit 0
fi
npm publish --tag "$NPM_TAG"

- name: Publish @bscript/cli to npm
working-directory: ./cli
env:
NPM_TAG: ${{ steps.npm_tag.outputs.tag }}
TAG_VERSION: ${{ steps.npm_tag.outputs.version }}
run: |
if npm view "@bscript/cli@${TAG_VERSION}" version > /dev/null 2>&1; then
echo "@bscript/cli@${TAG_VERSION} is already published; skipping."
exit 0
fi
npm publish --tag "$NPM_TAG"
107 changes: 83 additions & 24 deletions .github/workflows/release-website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,18 @@ jobs:

- name: Determine Version
id: determine_version
env:
EVENT_NAME: ${{ github.event_name }}
TAG_NAME: ${{ github.ref_name }}
RELEASE_IS_PRERELEASE: ${{ github.event.release.prerelease }}
INPUT_VERSION: ${{ inputs.version }}
run: |
# 1. Get raw version number (e.g. 2.0.5 or 2.1.0-alpha.1)
if [ "${{ github.event_name }}" == "release" ]; then
TAG_NAME=${{ github.ref_name }}
RAW_VERSION=${TAG_NAME#v}
IS_PRERELEASE="${{ github.event.release.prerelease }}"
if [ "$EVENT_NAME" = "release" ]; then
RAW_VERSION="${TAG_NAME#v}"
IS_PRERELEASE="$RELEASE_IS_PRERELEASE"
else
RAW_VERSION="${{ inputs.version }}"
RAW_VERSION="$INPUT_VERSION"
if [[ "$RAW_VERSION" == *-* ]]; then
IS_PRERELEASE="true"
else
Expand All @@ -53,8 +57,14 @@ jobs:
echo "Detected raw version: $RAW_VERSION"
echo "Pre-release: $IS_PRERELEASE"

# 2. Validate GitHub release type vs semver
if [ "${{ github.event_name }}" == "release" ]; then
# 2. Validate the version format
if ! [[ "$RAW_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]]; then
echo "::error::Invalid version: ${RAW_VERSION}. Expected format like 2.0.5 or 2.1.0-alpha.1"
exit 1
fi

# 3. Validate GitHub release type vs semver
if [ "$EVENT_NAME" = "release" ]; then
if [ "$IS_PRERELEASE" = "true" ] && [[ "$RAW_VERSION" != *-* ]]; then
echo "::error::GitHub pre-release requires a semver prerelease segment (e.g. 2.1.0-alpha.1)"
exit 1
Expand All @@ -65,25 +75,38 @@ jobs:
fi
fi

# 3. Convert to Docusaurus doc version
# 4. Convert to Docusaurus doc version
if [ "$IS_PRERELEASE" = "true" ]; then
DOC_VERSION=$(node -e "
const m = process.argv[1].match(/^(\d+\.\d+\.\d+)-([0-9A-Za-z-]+)/);
DOC_VERSION=$(node -e '
const version = process.argv[1];
const m = version.match(/^(\d+\.\d+\.\d+)-([0-9A-Za-z-]+)/);
if (!m) {
console.error('::error::Could not derive doc version from prerelease (${RAW_VERSION}). Expected format like 2.1.0-alpha.1');
console.error("::error::Could not derive doc version from prerelease (" + version + "). Expected format like 2.1.0-alpha.1");
process.exit(1);
}
process.stdout.write(m[1] + '-' + m[2].split('.')[0] + '.x');
" "$RAW_VERSION")
process.stdout.write(m[1] + "-" + m[2] + ".x");
' "$RAW_VERSION")
else
BASE_VER=${RAW_VERSION%.*}
DOC_VERSION="${BASE_VER}.x"
fi

# 5. Build the pull request texts. They contain ": ", which is not valid inside
# an unquoted YAML value in the create-pull-request step.
if [ "$IS_PRERELEASE" = "true" ]; then
PR_TITLE="docs: update pre-release version ${DOC_VERSION} (${RAW_VERSION})"
PR_BODY="Automated docs update for pre-release ${DOC_VERSION} (tag version ${RAW_VERSION})."
else
PR_TITLE="docs: update version ${DOC_VERSION}"
PR_BODY="Automated docs update for version ${DOC_VERSION}."
fi

echo "Target Docusaurus Version: $DOC_VERSION"
echo "doc_version=$DOC_VERSION" >> $GITHUB_OUTPUT
echo "is_prerelease=$IS_PRERELEASE" >> $GITHUB_OUTPUT
echo "raw_version=$RAW_VERSION" >> $GITHUB_OUTPUT
echo "doc_version=$DOC_VERSION" >> "$GITHUB_OUTPUT"
echo "is_prerelease=$IS_PRERELEASE" >> "$GITHUB_OUTPUT"
echo "raw_version=$RAW_VERSION" >> "$GITHUB_OUTPUT"
echo "pr_title=$PR_TITLE" >> "$GITHUB_OUTPUT"
echo "pr_body=$PR_BODY" >> "$GITHUB_OUTPUT"

- name: Remove prerelease doc versions on stable release
if: steps.determine_version.outputs.is_prerelease != 'true'
Expand Down Expand Up @@ -117,17 +140,33 @@ jobs:
fi
}

# e.g. stable 2.1.0 -> 2.1.0-alpha.x, 2.1.0-beta.x
# Any prerelease docs whose base version is at or below the stable release are
# superseded by it, e.g. stable 2.1.1 removes 2.1.0-alpha.x and 2.1.1-beta.x.
PRERELEASE_VERSIONS=$(
{
if [ -f "$VERSIONS_JSON" ]; then
jq -r --arg raw "$RAW_VERSION" '.[] | select(startswith($raw + "-") and endswith(".x"))' "$VERSIONS_JSON"
jq -r '.[]' "$VERSIONS_JSON"
fi
shopt -s nullglob
for dir in versioned_docs/version-"${RAW_VERSION}"-*.x; do
for dir in versioned_docs/version-*; do
basename "$dir" | sed 's/^version-//'
done
} | sort -u
} | sort -u | node -e '
const stable = process.env.RAW_VERSION.split("-")[0].split(".").map(Number);
const isSuperseded = (base) => {
for (let i = 0; i < 3; i++) {
if (base[i] !== stable[i]) return base[i] < stable[i];
}
return true;
};

for (const line of require("fs").readFileSync(0, "utf8").split("\n")) {
const docVersion = line.trim();
const m = docVersion.match(/^(\d+)\.(\d+)\.(\d+)-[0-9A-Za-z-]+\.x$/);
if (!m) continue;
if (isSuperseded(m.slice(1, 4).map(Number))) console.log(docVersion);
}
'
)

if [ -z "$PRERELEASE_VERSIONS" ]; then
Expand Down Expand Up @@ -170,20 +209,40 @@ jobs:
echo "Version $DOC_VERSION does not exist yet. Ready to create."
fi

# The pull request has to be based on main, but the snapshot must contain the docs
# as they were at the released tag, not whatever landed on main afterwards.
- name: Check Out Docs From the Released Tag
if: github.event_name == 'release'
working-directory: ./website
env:
TAG_NAME: ${{ github.ref_name }}
run: |
if git rev-parse --verify "refs/tags/${TAG_NAME}" > /dev/null 2>&1; then
git checkout "refs/tags/${TAG_NAME}" -- docs sidebars.ts
echo "Using docs from tag ${TAG_NAME}."
else
echo "::warning::Tag ${TAG_NAME} not found; falling back to the docs on main."
fi

- name: Run Docusaurus Versioning
working-directory: ./website
env:
DOC_VERSION: ${{ steps.determine_version.outputs.doc_version }}
run: |
echo "Creating version $DOC_VERSION..."
npm run docusaurus docs:version $DOC_VERSION
npm run docusaurus docs:version "$DOC_VERSION"

- name: Restore Docs From main
if: github.event_name == 'release'
working-directory: ./website
run: git restore --source=HEAD --staged --worktree -- docs sidebars.ts

- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
commit-message: ${{ steps.determine_version.outputs.is_prerelease == 'true' && format('docs: update pre-release version {0} ({1})', steps.determine_version.outputs.doc_version, steps.determine_version.outputs.raw_version) || format('docs: update version {0}', steps.determine_version.outputs.doc_version) }}
title: ${{ steps.determine_version.outputs.is_prerelease == 'true' && format('docs: update pre-release version {0} ({1})', steps.determine_version.outputs.doc_version, steps.determine_version.outputs.raw_version) || format('docs: update version {0}', steps.determine_version.outputs.doc_version) }}
body: ${{ steps.determine_version.outputs.is_prerelease == 'true' && format('Automated docs update for pre-release {0} (tag version {1}).', steps.determine_version.outputs.doc_version, steps.determine_version.outputs.raw_version) || format('Automated docs update for version {0}.', steps.determine_version.outputs.doc_version) }}
commit-message: ${{ steps.determine_version.outputs.pr_title }}
title: ${{ steps.determine_version.outputs.pr_title }}
body: ${{ steps.determine_version.outputs.pr_body }}
branch: docs-version-${{ steps.determine_version.outputs.doc_version }}
base: main
6 changes: 3 additions & 3 deletions cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bscript/cli",
"version": "2.0.2",
"version": "2.1.0-alpha.1",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"engines": {
Expand Down Expand Up @@ -60,8 +60,8 @@
},
"dependencies": {
"@abandonware/noble": "^1.9.2-26",
"@bscript/lang": "2.0.2",
"@bscript/notebook": "2.0.2",
"@bscript/lang": "2.1.0-alpha.1",
"@bscript/notebook": "2.1.0-alpha.1",
"axios": "^1.12.2",
"chalk": "^4.1.2",
"commander": "^14.0.1",
Expand Down
2 changes: 1 addition & 1 deletion lang/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bscript/lang",
"version": "2.0.2",
"version": "2.1.0-alpha.1",
"exports": "./dist/index.js",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
2 changes: 1 addition & 1 deletion notebook/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@bscript/notebook",
"version": "2.0.2",
"version": "2.1.0-alpha.1",
"engines": {
"node": ">=18.0.0"
},
Expand Down
Loading
Loading