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
2 changes: 2 additions & 0 deletions .github/workflows/claude-remediation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ jobs:
# Only PR comments (issue_comment fires for both issues and PRs) that
# contain the trigger phrase. This workflow never runs on a bare scan
# failure - a maintainer has to ask for it explicitly.
# Only the owner, members and collaborators can trigger it, so a stranger's comment cannot spend the API key.
if: >
github.event.issue.pull_request != null &&
contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association) &&
(contains(github.event.comment.body, '/remediate') ||
contains(github.event.comment.body, '/claude fix'))
runs-on: ubuntu-latest
Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/dependabot-automerge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,15 @@ jobs:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
steps:
- name: Read the update type
id: meta
uses: dependabot/fetch-metadata@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}

# Major version bumps wait for a person.
- name: Enable auto-merge
if: steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
Expand Down
37 changes: 35 additions & 2 deletions .github/workflows/deploy-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ on:
- 'demo-btree/**'
- 'demo-intent/**'
- 'intent/**'
- 'verify/**'
- 'inmemory/**'
- 'go.mod'
- 'go.sum'
- 'docs/assets/**'
- 'docs/index.html'
- 'scripts/assemble-site.sh'
Expand Down Expand Up @@ -153,6 +157,7 @@ jobs:
- uses: actions/setup-node@v7
with:
node-version: 20
cache: 'npm'

- name: Deploy
id: deploy
Expand All @@ -162,12 +167,12 @@ jobs:
MODE: ${{ github.event_name == 'workflow_dispatch' && inputs.mode || 'production' }}
run: |
if [ "$MODE" = production ]; then
npx --yes wrangler@4 deploy
npx --yes wrangler@4.151.3 deploy
echo "url=https://joltrinhq.com" >> "$GITHUB_OUTPUT"
else
# Wrangler writes a JSON line per event to this file, so the URL is read as a field.
export WRANGLER_OUTPUT_FILE_PATH="$RUNNER_TEMP/wrangler-output.json"
npx --yes wrangler@4 versions upload
npx --yes wrangler@4.151.3 versions upload
url=$(jq -r 'select(.type == "version-upload") | .preview_url // empty' "$WRANGLER_OUTPUT_FILE_PATH" | head -1)
[ -n "$url" ] || { echo "Error: no preview_url in wrangler output" >&2; exit 1; }
echo "url=$url" >> "$GITHUB_OUTPUT"
Expand Down Expand Up @@ -238,3 +243,31 @@ jobs:
curl -fsS "${SITE_URL}/" -o /tmp/live-index.html
mkdir -p /tmp/live && cp /tmp/live-index.html /tmp/live/index.html
scripts/check-youtube.sh /tmp/live

# Rollback. If the live site fails its smoke run right after a production deploy, put the
# previous Cloudflare version back so visitors are not left on a broken page. Preview
# uploads never reach production, so they are not rolled back. Run history shows which
# version came back; to do the same by hand: npx --yes wrangler@4.151.3 rollback --message "reason"
# (needs CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID). After a rollback, fix forward
# in a new commit. This job leaves the workflow red so the failure is not missed.
rollback:
name: Roll back a failed production deploy
needs: [deploy, verify-live]
if: ${{ always() && needs.verify-live.result == 'failure' && (github.event_name != 'workflow_dispatch' || inputs.mode == 'production') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7

- uses: actions/setup-node@v7
with:
node-version: 20
cache: 'npm'

- name: Roll back to the previous version
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
npx --yes wrangler@4.151.3 rollback --message "verify-live failed for ${GITHUB_SHA::7}" --yes
echo "Rolled back. Production serves the previous version; fix forward in a new commit." >> "$GITHUB_STEP_SUMMARY"
exit 1
16 changes: 14 additions & 2 deletions .github/workflows/uptime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
# /api/health (liveness) and /api/ready (readiness) and fails the run if either
# does not return 200 with the expected body after three tries. Run history is the availability record:
# gh run list --workflow uptime.yml --limit 500 --json conclusion
# GitHub's scheduler can delay or skip runs, so treat the history as a
# sample, not a complete measurement. It uses no secrets and changes nothing.
# It also opens https://joltrinhq.com/ (the marketing site on Cloudflare), which the Azure
# health endpoints do not cover. GitHub's scheduler can delay or skip runs, so this is not
# an SLO and the history is a sample, not a complete measurement. For a real availability
# target use an external monitor. It uses no secrets and changes nothing.
name: Live app check

on:
Expand Down Expand Up @@ -47,4 +49,14 @@ jobs:
echo "| $path | $code | $secs |" >> "$GITHUB_STEP_SUMMARY"
[ "$ok" = yes ] || { echo "$path failed: status $code, expected body with $want" >&2; fail=1; }
done
# The marketing site is served by Cloudflare, so it is probed separately from the Azure app.
code=000; secs=0; ok=no
for try in 1 2 3; do
out=$(curl -s -o /tmp/site --max-time 20 -w '%{http_code} %{time_total}' https://joltrinhq.com/ || true)
code=${out%% *}; secs=${out##* }
if [ "$code" = 200 ] && grep -q '<h1' /tmp/site; then ok=yes; break; fi
sleep 5
done
echo "| https://joltrinhq.com/ | $code | $secs |" >> "$GITHUB_STEP_SUMMARY"
[ "$ok" = yes ] || { echo "https://joltrinhq.com/ failed: status $code or no headline in the page" >&2; fail=1; }
exit $fail
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -283,3 +283,9 @@ playwright-report/
test-results/

ai/agent/agent

# Local working files that are not part of the project
.playwright-mcp/
linkedin-assets/
LINKEDIN_JOLTRIN_LAUNCH.md
STRIPE_SETUP_PROGRESS.md
6 changes: 6 additions & 0 deletions demo/.well-known/security.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Contact: https://github.com/SharedCode/joltrin/security/advisories/new
Contact: mailto:gerardrecinto@gmail.com
Expires: 2027-10-28T00:00:00.000Z
Preferred-Languages: en
Canonical: https://joltrinhq.com/.well-known/security.txt
Policy: https://github.com/SharedCode/joltrin/blob/master/.github/SECURITY.md
2 changes: 1 addition & 1 deletion deploy/cloudflare/_headers
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: SAMEORIGIN
Permissions-Policy: camera=(), microphone=(), geolocation=()
Strict-Transport-Security: max-age=31536000
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: base-uri 'self'; object-src 'none'; frame-ancestors 'self'; form-action 'self'

# Preview and workers.dev hostnames must never be indexed.
Expand Down
3 changes: 3 additions & 0 deletions scripts/assemble-site.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ cp demo-btree/index.html demo-btree/tailwind.css demo-btree/arena.wasm demo-btre
cp demo-intent/index.html demo-intent/tailwind.css demo-intent/intent.wasm demo-intent/wasm_exec.js demo-intent/favicon.svg demo-intent/favicon.ico demo-intent/og-image.png demo-intent/logo-mark.png "$SITE/private-intent/"
# Response headers for the Cloudflare deployment.
cp deploy/cloudflare/_headers deploy/cloudflare/_redirects "$SITE/"
# Security contact for researchers (RFC 9116).
mkdir -p "$SITE/.well-known"
cp demo/.well-known/security.txt "$SITE/.well-known/"

fail=0

Expand Down
Loading