Skip to content

nightly

nightly #6

Workflow file for this run

# ─────────────────────────────────────────────────────────────────────────────
# Nightly: is dev stable? Build it from scratch, every platform, and install it the
# way a user would. The workflow's badge in the README is dev's status.
#
# A green night is published to npm under the `nightly` dist-tag (never `latest`), so
# `npm i @axiomcode/code-graph@nightly` installs dev as of that night. Pull request
# runs test each change on its own; this is the only
# full check of dev's head as it actually is — after several merges, after direct
# pushes and sync-dev's merges from main, with the every-platform engine build and the
# install-and-run check that dev's pull requests skip, and against whatever versions
# of the dependencies a fresh install resolves today (no lock file is committed, so
# those move without a commit here).
#
# 1. CI with fresh=true: a clean checkout, no restored engine cache, every engine
# compiled from the rules, the five suites, and every platform's engines.
# 2. End to end: pack @axiomcode/code-graph and the linux-x64 engine package exactly
# as publish-npm.yml would, install them into an empty project with no Soufflé,
# and run axiomcode on a test project (.github/scripts/e2e-install.sh).
# 3. `npm publish --dry-run` for every package: the registry's own checks, no upload.
# 4. Green: publish every package as <next>-nightly.<date>.g<sha> under `nightly`.
# The version is computed here and never committed or tagged. <next> is the
# version after the last release, so a nightly always sorts above what is out
# and below the release it leads to. Skipped when that commit is already the
# nightly, and until a first real release exists: npm makes a package's first
# publish its `latest`, whatever dist-tag it was published under.
# 5. A failure opens an issue (or comments on the open one) naming what broke; the
# next green night closes it.
#
# Runs every night, new commits or not: the status is daily, and a dependency release
# can break a fresh install with nothing committed. Run it by hand from Actions any
# time. Scheduled runs use the default branch — dev — for the workflow and the code.
# ─────────────────────────────────────────────────────────────────────────────
name: nightly
on:
schedule:
- cron: '0 8 * * *' # 08:00 UTC, after the US workday
workflow_dispatch:
inputs:
publish:
description: publish this run under the nightly dist-tag if it is green
type: boolean
default: false
concurrency:
group: nightly
cancel-in-progress: false
permissions:
contents: read
jobs:
ci:
uses: ./.github/workflows/ci.yml
with:
fresh: true
e2e:
name: install and run, as a user
needs: ci
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
- run: npm install --no-audit --no-fund
- uses: actions/download-artifact@v4
with: { pattern: engines-*, path: artifacts }
- name: every language, installed from the tarballs, no Soufflé
run: |
set -euo pipefail
for pair in java:java typescript:typescript python:python javascript:javascript; do
lang="${pair%%:*}"
case_dir="$(ls -d graph/test/$lang/cases/*/src | head -1)"
bash .github/scripts/e2e-install.sh artifacts/engines-linux-x64 linux-x64 "$lang" "$case_dir"
done
- name: npm publish --dry-run, every package
run: |
set -euo pipefail
version="$(node .github/scripts/version.mjs get)"
for d in artifacts/engines-*; do
platform="${d#artifacts/engines-}"
bash packaging/assemble-engine-package.sh "$platform" "$version" "$d" "packages/engine-$platform" >/dev/null
( cd "packages/engine-$platform" && npm publish --dry-run --access public 2>&1 | tail -3 )
done
npm publish --dry-run --ignore-scripts --access public 2>&1 | tail -3
publish:
name: publish under nightly
needs: [ci, e2e]
if: github.event_name == 'schedule' || inputs.publish
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
- name: decide the nightly version
id: v
run: |
set -euo pipefail
sha="g$(git rev-parse --short=7 HEAD)"
latest="$(npm view @axiomcode/code-graph dist-tags.latest 2>/dev/null || true)"
if [ -z "$latest" ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"
echo "no release on npm yet: a first publish would become 'latest', so no nightly until one exists"
exit 0
fi
current="$(npm view @axiomcode/code-graph dist-tags.nightly 2>/dev/null || true)"
if [ "${current##*.}" = "$sha" ]; then
echo "skip=true" >> "$GITHUB_OUTPUT"; echo "$current is already this commit"; exit 0
fi
base="$(node .github/scripts/version.mjs get)"
# a released version moves on to the next patch; an unreleased one is previewed as itself
if git rev-parse --verify --quiet "refs/tags/v$base" >/dev/null || [ "$base" = "$latest" ]; then
base="$(node -p 'const [a,b,c]=process.argv[1].split("-")[0].split(".").map(Number); `${a}.${b}.${c+1}`' "$base")"
fi
version="$base-nightly.$(date -u +%Y%m%d).$sha"
echo "version=$version" >> "$GITHUB_OUTPUT"; echo "skip=false" >> "$GITHUB_OUTPUT"
echo "nightly version: $version (latest release: $latest)"
- if: steps.v.outputs.skip == 'false'
run: |
node .github/scripts/version.mjs set "${{ steps.v.outputs.version }}"
npm install --no-audit --no-fund
- if: steps.v.outputs.skip == 'false'
uses: actions/download-artifact@v4
with: { pattern: engines-*, path: artifacts }
# Engines first: the root package pins them at its own version.
- name: publish every package under nightly
if: steps.v.outputs.skip == 'false'
env:
NODE_AUTH_TOKEN: ${{ secrets.CLI_BINARY_PUBLISH }}
VERSION: ${{ steps.v.outputs.version }}
run: |
set -euo pipefail
for d in artifacts/engines-*; do
platform="${d#artifacts/engines-}"
bash packaging/assemble-engine-package.sh "$platform" "$VERSION" "$d" "packages/engine-$platform" >/dev/null
( cd "packages/engine-$platform" && npm publish --access public --tag nightly )
done
npm publish --ignore-scripts --access public --tag nightly
echo "::notice::published $VERSION under nightly — npm i @axiomcode/code-graph@nightly"
report:
name: report
needs: [ci, e2e, publish]
if: always()
runs-on: ubuntu-24.04
permissions:
issues: write
steps:
- uses: actions/checkout@v4
with:
sparse-checkout: .github/actions
- name: the bot this job writes as
id: bot
uses: ./.github/actions/bot
with:
app-id: ${{ vars.AXIOMCODE_BOT_APP_ID }}
private-key: ${{ secrets.AXIOMCODE_BOT_PRIVATE_KEY }}
- env:
GH_TOKEN: ${{ steps.bot.outputs.token }}
GH_REPO: ${{ github.repository }}
CI_RESULT: ${{ needs.ci.result }}
E2E_RESULT: ${{ needs.e2e.result }}
PUBLISH_RESULT: ${{ needs.publish.result }}
run: |
set -uo pipefail
title="nightly build of dev is failing"
run_url="$GITHUB_SERVER_URL/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
existing="$(gh issue list --state open --search "\"$title\" in:title" --json number --jq '.[0].number' || true)"
if [ "$CI_RESULT" = success ] && [ "$E2E_RESULT" = success ] && [ "$PUBLISH_RESULT" != failure ]; then
echo "nightly green"
if [ -n "$existing" ]; then
gh issue close "$existing" --comment "Green again at ${GITHUB_SHA::8}: $run_url"
fi
exit 0
fi
{
echo "Nightly build of \`${GITHUB_REF_NAME}\` at ${GITHUB_SHA::8} failed."
echo
echo "| stage | result |"
echo "|---|---|"
echo "| build, tests, every platform's engines (fresh) | $CI_RESULT |"
echo "| install from tarballs and run; npm publish dry-run | $E2E_RESULT |"
echo "| publish under nightly | $PUBLISH_RESULT |"
echo
echo "Run: $run_url"
echo
echo "A nightly is published only when every stage passes. This closes itself on the next green night."
} > /tmp/body.md
if [ -n "$existing" ]; then
gh issue comment "$existing" --body-file /tmp/body.md
else
gh issue create --title "$title" --body-file /tmp/body.md
fi
exit 1