Make transcripts idempotent #9624
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: CI | |
defaults: | |
run: | |
shell: bash | |
on: | |
# Run on the post-merge result of every PR commit | |
pull_request: | |
# Build on the pre-merge result of every branch commit | |
push: | |
workflow_dispatch: | |
env: | |
## Some version numbers that are used during CI | |
ormolu_version: 0.7.2.0 | |
jit_version: "@unison/internal/releases/0.0.25" | |
runtime_tests_version: "@unison/runtime-tests/releases/0.0.1" | |
## Some cached directories | |
# a temp path for caching a built `ucm` | |
ucm_local_bin: ucm-local-bin | |
# a temp path for caching generated jit sources | |
jit_src_scheme: unison-jit-src/scheme-libs/racket | |
jit_dist: unison-jit-dist | |
# a codebase path for caching a codebase generated by `unison-src/builtin-tests/interpreter-tests.md` | |
runtime_tests_codebase: "~/.cache/unisonlanguage/runtime-tests.unison" | |
# locations of some files that will indicate whether we need to re-run certain steps | |
transcript_test_results: transcript-test-results | |
interpreter_test_results: interpreter-test-results | |
## afaik, the jit sources are generated in a platform-independent way, so we choose one platform to generate them on. | |
jit_generator_os: ubuntu-20.04 | |
jobs: | |
ormolu: | |
runs-on: ubuntu-20.04 | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Get changed files | |
uses: tj-actions/changed-files@v44 | |
with: | |
# globs copied from default settings for run-ormolu | |
files: | | |
**/*.hs | |
**/*.hs-boot | |
separator: "\n" | |
- uses: haskell-actions/run-ormolu@v15 | |
with: | |
version: ${{ env.ormolu_version }} | |
mode: inplace | |
pattern: ${{ steps.changed-files.outputs.all_changed_files }} | |
- name: apply formatting changes | |
uses: stefanzweifel/git-auto-commit-action@v5 | |
# Only try to commit formatting changes if we're running within the repo containing the PR, | |
# and not on a protected branch. | |
# The job doesn't have permission to push back to contributor forks on contributor PRs. | |
if: | | |
always() | |
&& !github.ref_protected | |
&& github.event.pull_request.base.repo.full_name == github.event.pull_request.head.repo.full_name | |
with: | |
commit_message: automatically run ormolu | |
build-ucm: | |
name: build ucm | |
runs-on: ${{ matrix.os }} | |
strategy: | |
# Run each build to completion, regardless of if any have failed | |
fail-fast: false | |
matrix: | |
os: | |
# While iterating on this file, you can disable one or more of these to speed things up | |
- ubuntu-20.04 | |
- macOS-13 | |
- windows-2019 | |
# - windows-2022 | |
steps: | |
- uses: actions/checkout@v4 | |
- name: tweak environment | |
id: tweak-environment | |
run: | | |
ucm_local_bin="${RUNNER_TEMP//\\//}/${ucm_local_bin}" | |
echo "ucm_local_bin=$ucm_local_bin" >> $GITHUB_ENV | |
if [[ ${{runner.os}} = "Windows" ]]; then | |
echo "ucm=$ucm_local_bin/unison.exe" >> $GITHUB_ENV | |
echo "transcripts=$ucm_local_bin/transcripts.exe" >> $GITHUB_ENV | |
else | |
echo "ucm=$ucm_local_bin/unison" >> $GITHUB_ENV | |
echo "transcripts=$ucm_local_bin/transcripts" >> $GITHUB_ENV | |
fi | |
- name: cache ucm binaries | |
id: cache-ucm-binaries | |
uses: actions/cache@v4 | |
with: | |
path: ${{env.ucm_local_bin}} | |
key: ucm-${{ matrix.os }}-${{ hashFiles('**/stack.yaml', '**/package.yaml', '**/*.hs', '**/unison-cli-integration/integrationtests/IntegrationTests/*')}} | |
# added the integration test dependencies here as if they were source, for simplicity | |
- name: restore stack caches | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
id: restore-stack-caches | |
uses: unisonweb/actions/stack/cache/restore@main | |
with: | |
cache-prefix: ci | |
- name: install stack | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
uses: unisonweb/actions/stack/install@main | |
with: | |
stack-version: 2.15.5 | |
# Build deps, then build local code. Splitting it into two steps just allows us to see how much time each step | |
# takes. | |
- name: build dependencies | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
# Run up to 5 times in a row before giving up. | |
# It's very unlikely that our build-dependencies step will fail on most builds, | |
# so if it fails its almost certainly due to a race condition on the Windows | |
# file-system API that stack runs into. Since any successful packages are | |
# cached within a single build, it should get further along on each re-start | |
# and should hopefully finish! | |
run: | | |
stack --version | |
tries=1 | |
if [[ ${{matrix.os}} = "windows-"* ]]; then | |
tries=5 | |
fi | |
for (( i = 0; i < $tries; i++ )); do | |
stack build --fast --only-dependencies --test --bench && break; | |
done | |
- name: build | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: | | |
stack build \ | |
--fast \ | |
--test \ | |
--no-run-tests \ | |
--local-bin-path ${{env.ucm_local_bin}} \ | |
--copy-bins | |
# The unison-cli test requires a git user. | |
- name: set git user info | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: | | |
git config --global user.name "GitHub Actions" | |
git config --global user.email "[email protected]" | |
# Run each test suite (tests and transcripts) | |
- name: unison-cli test | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: stack build --fast --test unison-cli | |
- name: unison-core tests | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: stack build --fast --test unison-core | |
- name: unison-parser-typechecker tests | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: stack build --fast --test unison-parser-typechecker | |
- name: unison-sqlite tests | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: stack build --fast --test unison-sqlite | |
- name: unison-syntax tests | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: stack build --fast --test unison-syntax | |
- name: unison-util-bytes tests | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: stack build --fast --test unison-util-bytes | |
- name: unison-util-cache tests | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: stack build --fast --test unison-util-cache | |
- name: unison-util-relation tests | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: stack build --fast --test unison-util-relation | |
- name: cli-integration-tests | |
if: steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: stack exec cli-integration-tests | |
- name: verify stack ghci startup | |
if: runner.os == 'macOS' && steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
run: echo | stack ghci | |
- name: save ucm artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: unison-${{ matrix.os }} | |
path: ${{ env.ucm }} | |
if-no-files-found: error | |
- name: save stack caches | |
if: | | |
!cancelled() | |
&& steps.restore-stack-caches.outputs.cache-hit != 'true' | |
&& steps.cache-ucm-binaries.outputs.cache-hit != 'true' | |
uses: unisonweb/actions/stack/cache/save@main | |
with: | |
cache-prefix: ci | |
transcripts: | |
name: run transcripts | |
needs: build-ucm | |
runs-on: ${{ matrix.os }} | |
strategy: | |
# Run each build to completion, regardless of if any have failed | |
fail-fast: false | |
matrix: | |
os: | |
# While iterating on this file, you can disable one or more of these to speed things up | |
- ubuntu-20.04 | |
- macos-13 | |
- windows-2019 | |
# - windows-2022 | |
steps: | |
- uses: actions/checkout@v4 | |
- name: tweak environment | |
run: | | |
transcript_test_results="${RUNNER_TEMP//\\//}/${transcript_test_results}" | |
echo "transcript_test_results=$transcript_test_results" >> $GITHUB_ENV | |
ucm_local_bin="${RUNNER_TEMP//\\//}/${ucm_local_bin}" | |
echo "ucm_local_bin=$ucm_local_bin" >> $GITHUB_ENV | |
if [[ ${{runner.os}} = "Windows" ]]; then | |
echo "ucm=$ucm_local_bin/unison.exe" >> $GITHUB_ENV | |
echo "transcripts=$ucm_local_bin/transcripts.exe" >> $GITHUB_ENV | |
else | |
echo "ucm=$ucm_local_bin/unison" >> $GITHUB_ENV | |
echo "transcripts=$ucm_local_bin/transcripts" >> $GITHUB_ENV | |
fi | |
- name: cache transcript test results | |
id: cache-transcript-test-results | |
uses: actions/cache@v4 | |
with: | |
path: ${{env.transcript_test_results}} | |
key: transcripts-results-${{ matrix.os }}-${{ hashFiles('**/stack.yaml', '**/package.yaml', '**/*.hs')}}-${{ hashFiles('**/unison-src/**/*.md', '**/unison-src/**/*.u') }} | |
- name: restore binaries | |
uses: actions/cache/restore@v4 | |
if: steps.cache-transcript-test-results.outputs.cache-hit != 'true' | |
with: | |
path: ${{env.ucm_local_bin}} | |
key: ucm-${{ matrix.os }}-${{ hashFiles('**/stack.yaml', '**/package.yaml', '**/*.hs')}} | |
fail-on-cache-miss: true | |
# One of the transcripts fails if the user's git name hasn't been set. | |
## (Which transcript? -AI) | |
- name: set git user info | |
run: | | |
git config --global user.name "GitHub Actions" | |
git config --global user.email "[email protected]" | |
- name: round-trip-tests | |
if: steps.cache-transcript-test-results.outputs.cache-hit != 'true' | |
run: | | |
${{env.ucm}} transcript unison-src/transcripts-round-trip/main.md | |
${{env.ucm}} transcript unison-src/transcripts-manual/rewrites.md | |
# Fail if any transcripts cause git diffs. | |
git diff --ignore-cr-at-eol --exit-code \ | |
unison-src/transcripts-round-trip/main.output.md \ | |
unison-src/transcripts-manual/rewrites.output.md | |
- name: transcripts | |
if: steps.cache-transcript-test-results.outputs.cache-hit != 'true' | |
run: | | |
${{env.transcripts}} | |
# Fail if any transcripts cause git diffs. | |
git diff --ignore-cr-at-eol --exit-code unison-src/transcripts | |
- name: docs.to-html | |
if: steps.cache-transcript-test-results.outputs.cache-hit != 'true' | |
run: | | |
${{env.ucm}} transcript unison-src/transcripts-manual/docs.to-html.md | |
# Fail if the output or generated docs differ. | |
git diff --ignore-cr-at-eol --exit-code \ | |
unison-src/transcripts-manual/docs.to-html.output.md \ | |
unison-src/transcripts-manual/docs.to-html | |
- name: mark transcripts as passing | |
if: steps.cache-transcript-test-results.outputs.cache-hit != 'true' | |
run: | | |
echo "passing=true" >> "${{env.transcript_test_results}}" | |
interpreter-tests: | |
name: run interpreter tests | |
needs: build-ucm | |
runs-on: ${{ matrix.os }} | |
strategy: | |
# Run each build to completion, regardless of if any have failed | |
fail-fast: false | |
matrix: | |
os: | |
# While iterating on this file, you can disable one or more of these to speed things up | |
- ubuntu-20.04 | |
- macos-13 | |
# - windows-2019 | |
# - windows-2022 | |
steps: | |
- uses: actions/checkout@v4 | |
- name: tweak environment | |
run: | | |
interpreter_test_results="${RUNNER_TEMP//\\//}/${interpreter_test_results}" | |
echo "interpreter_test_results=$interpreter_test_results" >> $GITHUB_ENV | |
ucm_local_bin="${RUNNER_TEMP//\\//}/${ucm_local_bin}" | |
echo "ucm_local_bin=$ucm_local_bin" >> $GITHUB_ENV | |
if [[ ${{runner.os}} = "Windows" ]]; then | |
echo "ucm=$ucm_local_bin/unison.exe" >> $GITHUB_ENV | |
echo "transcripts=$ucm_local_bin/transcripts.exe" >> $GITHUB_ENV | |
else | |
echo "ucm=$ucm_local_bin/unison" >> $GITHUB_ENV | |
echo "transcripts=$ucm_local_bin/transcripts" >> $GITHUB_ENV | |
fi | |
- name: look up hash for runtime tests | |
run: echo "runtime_tests_causalhash=$(scripts/get-share-hash.sh ${{env.runtime_tests_version}})" >> $GITHUB_ENV | |
- name: cache interpreter test results | |
id: cache-interpreter-test-results | |
uses: actions/cache@v4 | |
with: | |
path: ${{env.interpreter_test_results}} | |
key: interpreter-test-results-${{ matrix.os }}-${{ hashFiles('**/stack.yaml', '**/package.yaml', '**/*.hs')}}-${{ hashFiles('**/interpreter-tests.tpl.md') }}-${{env.runtime_tests_causalhash}} | |
- name: cache testing codebase | |
id: cache-testing-codebase | |
if: steps.cache-interpreter-test-results.outputs.cache-hit != 'true' | |
uses: actions/cache@v4 | |
with: | |
path: ${{ env.runtime_tests_codebase }} | |
key: runtime-tests-codebase-${{env.runtime_tests_causalhash}} | |
restore-keys: runtime-tests-codebase- | |
- name: restore binaries | |
uses: actions/cache/restore@v4 | |
if: steps.cache-interpreter-test-results.outputs.cache-hit != 'true' | |
with: | |
path: ${{env.ucm_local_bin}} | |
key: ucm-${{ matrix.os }}-${{ hashFiles('**/stack.yaml', '**/package.yaml', '**/*.hs')}} | |
fail-on-cache-miss: true | |
- name: interpreter tests | |
# this one should be re-run if the ucm binaries have changed or unison-src/ has changed | |
if: steps.cache-interpreter-test-results.outputs.cache-hit != 'true' | |
run: | | |
envsubst '${runtime_tests_version}' \ | |
< unison-src/builtin-tests/interpreter-tests.tpl.md \ | |
> unison-src/builtin-tests/interpreter-tests.md | |
${{ env.ucm }} transcript.fork -C ${{ env.runtime_tests_codebase }} unison-src/builtin-tests/interpreter-tests.md | |
cat unison-src/builtin-tests/interpreter-tests.output.md | |
git diff --exit-code unison-src/builtin-tests/interpreter-tests.output.md | |
- name: mark interpreter tests as passing | |
if: steps.cache-interpreter-test-results.outputs.cache-hit != 'true' | |
run: | | |
echo "passing=true" >> "${{env.interpreter_test_results}}" | |
generate-jit-source: | |
if: always() && needs.build-ucm.result == 'success' | |
name: generate jit source | |
needs: build-ucm | |
runs-on: ubuntu-20.04 | |
steps: | |
- name: set up environment | |
run: | | |
echo "jit_generated_src_scheme=${{ runner.temp }}/jit-generated-src" >> $GITHUB_ENV | |
echo "jit_src_scheme=${{ runner.temp }}/${{ env.jit_src_scheme }}" >> $GITHUB_ENV | |
echo "ucm=${{ runner.temp }}/unison" >> $GITHUB_ENV | |
- name: download scheme-libs | |
uses: actions/checkout@v4 | |
with: | |
sparse-checkout: | | |
scripts/get-share-hash.sh | |
scheme-libs | |
- name: look up hash for jit source generator | |
run: echo "jit_causalhash=$(scripts/get-share-hash.sh ${{env.jit_version}})" >> $GITHUB_ENV | |
- uses: actions/cache@v4 | |
name: cache auto-generated jit source | |
id: cache-generated-source | |
with: | |
path: ${{ env.jit_generated_src_scheme }} | |
key: jit_generated_src_scheme-jit_${{env.jit_version}}-${{env.jit_causalhash}} | |
- name: create transcript | |
if: steps.cache-generated-source.outputs.cache-hit != 'true' | |
uses: DamianReeves/[email protected] | |
with: | |
path: ${{ runner.temp }}/setup-jit.md | |
write-mode: overwrite | |
contents: | | |
``` ucm | |
scratch/main> project.create-empty jit-setup | |
jit-setup/main> lib.install ${{ env.jit_version }} | |
``` | |
``` unison | |
go = generateSchemeBoot "${{ env.jit_generated_src_scheme }}" | |
``` | |
``` ucm | |
jit-setup/main> run go | |
``` | |
- name: download ucm artifact | |
if: steps.cache-generated-source.outputs.cache-hit != 'true' | |
uses: actions/download-artifact@v4 | |
with: | |
name: unison-${{ env.jit_generator_os }} | |
path: ${{ runner.temp }} | |
- name: set ucm permissions | |
if: steps.cache-generated-source.outputs.cache-hit != 'true' | |
run: chmod +x ${{ env.ucm }} | |
- name: generate source | |
if: steps.cache-generated-source.outputs.cache-hit != 'true' | |
run: | | |
mkdir -p ${{ env.jit_generated_src_scheme }} | |
${{ env.ucm }} transcript ${{ runner.temp }}/setup-jit.md | |
- name: bundle source | |
run: | | |
mkdir -p ${{ env.jit_src_scheme }} | |
cp -R scheme-libs/racket/* ${{ env.jit_src_scheme }} | |
cp -R "${{ env.jit_generated_src_scheme }}"/* ${{ env.jit_src_scheme }} | |
- name: save jit source | |
if: always() | |
uses: actions/upload-artifact@v4 | |
with: | |
name: jit-source | |
path: ${{ env.jit_src_scheme }}/** | |
if-no-files-found: error | |
build-jit-binary: | |
name: build jit binary | |
needs: generate-jit-source | |
uses: ./.github/workflows/ci-build-jit-binary.yaml | |
test-jit: | |
name: test jit | |
needs: build-jit-binary | |
uses: ./.github/workflows/ci-test-jit.yaml |