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
90 changes: 90 additions & 0 deletions .github/workflows/publish-dist.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Publish dist branch

# Builds the CSS bundles after every merge to main and force-pushes the
# resulting dist/ tree to a dedicated `dist` branch. The `dist` branch is
# orphan (no shared history with main) and contains only the generated
# bundles at its root, so consumers can reference them via:
#
# https://cdn.jsdelivr.net/gh/codeslash-dev/SLASHED@dist/slashed.essential.min.css
#
# Source files (core/, optional/, etc.) live only on main; built files live
# only on dist. This keeps main free of generated artifacts while still
# giving the CDN something to serve.

on:
push:
branches: [main]
workflow_dispatch:

# If several merges land in quick succession, cancel any in-flight publish
# and rerun against the newest commit only. Prevents racing force-pushes.
concurrency:
group: publish-dist
cancel-in-progress: true

permissions:
contents: write

jobs:
publish-dist:
name: Build and force-push dist branch
runs-on: ubuntu-latest
steps:
- name: Checkout main
uses: actions/checkout@v4

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/publish-dist.yml"
echo "== File: $FILE =="
if [ -f "$FILE" ]; then
  nl -ba "$FILE" | sed -n '1,160p'
else
  echo "Missing $FILE"
fi

echo
echo "== Extract uses: lines =="
rg -n '^\s*uses:\s*actions/(checkout|setup-node)@' .github/workflows/publish-dist.yml || true

Repository: codeslash-dev/SLASHED

Length of output: 173


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/publish-dist.yml"
echo "== File: $FILE =="

if [ -f "$FILE" ]; then
  # Print with line numbers without relying on `nl`
  awk '{printf "%5d\t%s\n", NR, $0}' "$FILE" | sed -n '1,220p'
else
  echo "Missing $FILE"
fi

echo
echo "== Extract uses: lines =="
rg -n '^\s*uses:\s*actions/(checkout|setup-node)@' "$FILE" || true

Repository: codeslash-dev/SLASHED

Length of output: 3307


Pin GitHub Actions to immutable SHAs.

.github/workflows/publish-dist.yml uses mutable tags:

  • uses: actions/checkout@v4 (line 34)
  • uses: actions/setup-node@v4 (line 39)
Suggested fix
-      - name: Checkout main
-        uses: actions/checkout@v4
+      - name: Checkout main
+        uses: actions/checkout@<full-length-commit-sha> # actions/checkout v4.x

-      - name: Setup Node
-        uses: actions/setup-node@v4
+      - name: Setup Node
+        uses: actions/setup-node@<full-length-commit-sha> # actions/setup-node v4.x
🧰 Tools
πŸͺ› zizmor (1.25.2)

[error] 34-34: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/publish-dist.yml at line 34, Replace the mutable action
tags with immutable commit SHAs: locate the two occurrences "uses:
actions/checkout@v4" and "uses: actions/setup-node@v4" and change them to the
corresponding pinned SHAs (e.g., "uses: actions/checkout@<commit-sha>" and
"uses: actions/setup-node@<commit-sha>") by copying the latest stable commit SHA
from each action's GitHub repository (or marketplace) and updating the workflow;
commit the change and verify the workflow runs successfully.

with:
fetch-depth: 1
Comment thread
coderabbitai[bot] marked this conversation as resolved.
# Don't write GITHUB_TOKEN into .git/config; the build steps
# (npm install, npm run build) shouldn't have implicit push
# credentials. We re-authenticate explicitly at push time below.
persist-credentials: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm

- name: Install dependencies
run: npm ci

- name: Build dist bundles
run: npm run build

- name: Stage dist for orphan branch
run: |
set -euo pipefail
mkdir -p /tmp/sf-dist
cp -R dist/. /tmp/sf-dist/
# Stamp file so consumers can verify exactly which source commit
# produced this dist tree.
{
echo "source-commit: ${GITHUB_SHA}"
echo "source-ref: ${GITHUB_REF}"
echo "built-at: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "built-by: .github/workflows/publish-dist.yml"
} > /tmp/sf-dist/SOURCE.txt

- name: Force-push dist branch
env:
# Scoped to this step only β€” checkout was started with
# persist-credentials: false, so GITHUB_TOKEN never landed in
# .git/config and never leaked into the build/install steps.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GIT_AUTHOR_NAME: github-actions[bot]
GIT_AUTHOR_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
GIT_COMMITTER_NAME: github-actions[bot]
GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
run: |
set -euo pipefail
git checkout --orphan dist
# The orphan checkout still stages main's tree; remove it all so
# only built artifacts end up on the dist branch.
git rm -rf . > /dev/null
shopt -s dotglob
cp -R /tmp/sf-dist/. .
git add -A
git commit -m "build: dist for ${GITHUB_SHA}"
git push --force \
"https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" \
dist
5 changes: 4 additions & 1 deletion docs/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SLASHED β€” Demo</title>
<!-- SLASHED via jsDelivr CDN -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/codeslash-dev/SLASHED@main/dist/slashed.essential.css">
<!-- Built bundles live on the orphan `dist` branch, auto-published by
.github/workflows/publish-dist.yml on every merge to main.
Source files (e.g. optional/*) live on main. -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/codeslash-dev/SLASHED@dist/slashed.essential.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/codeslash-dev/SLASHED@main/optional/tokens.palette.css">
<style>
/* ── demo-only overrides ──────────────────────────────── */
Expand Down
Loading