Publish Capture v1.0.0 release asset with checksum-pinned download #20
This file contains hidden or 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 | |
| on: | |
| push: | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| unit: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [windows-latest, ubuntu-latest] | |
| python-version: ["3.11", "3.12"] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - run: python -m pip install --no-deps . | |
| - run: scriptorium --version | |
| - run: scriptorium init --help | |
| - run: scriptorium inventory --help | |
| - run: scriptorium migrate --help | |
| - run: scriptorium resume --help | |
| - run: scriptorium status --help | |
| - run: scriptorium doctor --help | |
| - run: scriptorium host install --help | |
| - run: scriptorium components --profile core --json | |
| - run: scriptorium install core --target ./scriptorium-components-preview --json | |
| - run: python -m unittest discover -s tests -v | |
| - name: Run general project intake acceptance | |
| run: python tests/e2e_general_intake.py | |
| e2e-windows: | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| path: scriptorium | |
| persist-credentials: false | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| ref: v0.1.0 | |
| path: scriptorium-v0.1.0 | |
| persist-credentials: false | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| repository: scriptorium-suite/scriptorium-spec | |
| ref: 36612e80c49f5a8f346a09ed73f4b1c67cef5135 | |
| path: scriptorium-spec | |
| persist-credentials: false | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| repository: scriptorium-suite/steward | |
| ref: 7aafad1b432207905072c769fe527a3d4d1b365e | |
| path: steward | |
| persist-credentials: false | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| repository: foxsplendid/Provenance | |
| ref: 1e3f1c022148c33f257d8a27502b50dbe906611d | |
| path: Provenance | |
| persist-credentials: false | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| repository: foxsplendid/Academic-Slides-Agent | |
| ref: 6371f1e6eac41f505aed39e58dafb689317cab65 | |
| path: Academic-Slides-Agent | |
| persist-credentials: false | |
| - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: "3.12" | |
| - name: Install pinned build backend | |
| run: >- | |
| python -m pip install | |
| --disable-pip-version-check | |
| --no-deps | |
| --only-binary=:all: | |
| "setuptools==83.0.0" | |
| - name: Verify clean Windows install lifecycle | |
| shell: pwsh | |
| run: | | |
| python .\scriptorium\tests\e2e_install_lifecycle.py ` | |
| --scriptorium-root .\scriptorium ` | |
| --spec-root .\scriptorium-spec ` | |
| --steward-root .\steward ` | |
| --provenance-root .\Provenance ` | |
| --previous-scriptorium-root .\scriptorium-v0.1.0 ` | |
| --require-windows ` | |
| --report .\install-lifecycle-report.json | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| $report = Get-Content -LiteralPath .\install-lifecycle-report.json -Raw | ConvertFrom-Json | |
| if ( | |
| $report.status -ne "passed" -or | |
| $report.lifecycle.clean_install -ne "passed" -or | |
| $report.lifecycle.uninstall -ne "passed" -or | |
| $report.lifecycle.reinstall -ne "passed" -or | |
| $report.lifecycle.standalone_components -ne "passed" -or | |
| $report.lifecycle.doctor_and_demo -ne "passed" -or | |
| $report.lifecycle.version_transition.status -ne "passed" -or | |
| $report.lifecycle.version_transition.previous_version -ne "0.1.0" | |
| ) { | |
| throw "Clean Windows install lifecycle did not pass" | |
| } | |
| - name: Install uv for Lectern | |
| uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 | |
| with: | |
| version: "0.11.16" | |
| python-version: "3.12.3" | |
| - name: Install source checkouts | |
| shell: pwsh | |
| run: | | |
| python -m venv .demo-venv | |
| .\.demo-venv\Scripts\python.exe -m pip install --no-deps .\scriptorium | |
| .\.demo-venv\Scripts\python.exe -m pip install --no-deps .\steward | |
| .\.demo-venv\Scripts\python.exe -m pip install --no-deps .\Provenance | |
| - name: Install Lectern workspace without provider extras | |
| run: uv sync --project .\Academic-Slides-Agent --all-packages --locked --no-dev | |
| - name: Smoke installed public pull entries | |
| shell: pwsh | |
| run: | | |
| & .\.demo-venv\Scripts\prov-sync-pull.exe --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| & .\.demo-venv\Scripts\prov-sync-pending.exe --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| & .\.demo-venv\Scripts\prov-sync-fill.exe --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| & .\.demo-venv\Scripts\prov-sync-unresolved.exe --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| & .\.demo-venv\Scripts\prov-ingest-research.exe --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| $contextVersion = & .\.demo-venv\Scripts\prov-context.exe --version | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| if (($contextVersion -join "`n").Trim() -ne '0.18.0') { | |
| throw "Installed Provenance context runtime version mismatch" | |
| } | |
| $raw = & .\.demo-venv\Scripts\prov-sync-pull.exe --capabilities --json | |
| $code = $LASTEXITCODE | |
| if ($code -ne 0) { throw "Provenance capability probe exited $code" } | |
| $capabilities = ($raw -join "`n") | ConvertFrom-Json | |
| if ($capabilities.operation -ne "pull.capabilities") { | |
| throw "Unexpected Provenance capability operation" | |
| } | |
| if ($capabilities.generated_by.name -ne "provenance") { | |
| throw "Unexpected Provenance capability producer" | |
| } | |
| if ($capabilities.capabilities.pull_lock -ne $true) { | |
| throw "Installed Provenance lacks pull-lock support" | |
| } | |
| & .\.demo-venv\Scripts\scriptorium.exe pull --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| & .\.demo-venv\Scripts\scriptorium.exe inventory --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| & .\.demo-venv\Scripts\scriptorium.exe migrate --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| & .\.demo-venv\Scripts\scriptorium.exe status --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| & .\.demo-venv\Scripts\scriptorium.exe resume --help | Out-Null | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| $errorRaw = & .\.demo-venv\Scripts\scriptorium.exe pull --json 2>$null | |
| $errorCode = $LASTEXITCODE | |
| if ($errorCode -ne 2) { | |
| throw "Expected JSON usage error exit 2, received $errorCode" | |
| } | |
| $errorReport = ($errorRaw -join "`n") | ConvertFrom-Json | |
| if ( | |
| $errorReport.operation -ne "pull" -or | |
| $errorReport.status -ne "error" -or | |
| $errorReport.exit_code -ne 2 | |
| ) { | |
| throw "JSON usage-error envelope changed" | |
| } | |
| $global:LASTEXITCODE = 0 | |
| - name: Smoke installed content-free inventory | |
| shell: pwsh | |
| run: | | |
| $source = Join-Path $env:RUNNER_TEMP 'inventory source α' | |
| $conversation = Join-Path $env:RUNNER_TEMP 'inventory conversations' | |
| $zotero = Join-Path $env:RUNNER_TEMP 'inventory zotero' | |
| New-Item -ItemType Directory -Path $source, $conversation, $zotero | Out-Null | |
| [IO.File]::WriteAllText((Join-Path $source 'private-note.md'), 'PRIVATE_RESEARCH_SENTINEL') | |
| [IO.File]::WriteAllText((Join-Path $source 'private-paper.pdf'), 'PRIVATE_PDF_SENTINEL') | |
| [IO.File]::WriteAllText((Join-Path $conversation 'chat-history.zip'), 'PRIVATE_CHAT_SENTINEL') | |
| [IO.File]::WriteAllText((Join-Path $zotero 'library.bib'), 'PRIVATE_ZOTERO_SENTINEL') | |
| $raw = & .\.demo-venv\Scripts\scriptorium.exe inventory ` | |
| --source $source ` | |
| --conversation-export $conversation ` | |
| --zotero-export $zotero ` | |
| --json | |
| $code = $LASTEXITCODE | |
| if ($code -ne 0) { throw "Installed inventory exited $code" } | |
| $serialized = $raw -join "`n" | |
| $report = $serialized | ConvertFrom-Json | |
| if ( | |
| $report.operation -ne 'inventory' -or | |
| $report.status -ne 'planned' -or | |
| $report.summary.candidates -ne 4 | |
| ) { | |
| throw "Installed inventory report changed" | |
| } | |
| foreach ($privateValue in @( | |
| $source, | |
| $conversation, | |
| $zotero, | |
| 'private-note.md', | |
| 'private-paper.pdf', | |
| 'chat-history.zip', | |
| 'library.bib', | |
| 'PRIVATE_RESEARCH_SENTINEL' | |
| )) { | |
| if ($serialized.Contains($privateValue)) { | |
| throw "Inventory report exposed a private source value" | |
| } | |
| } | |
| if ([IO.File]::ReadAllText((Join-Path $source 'private-note.md')) -ne 'PRIVATE_RESEARCH_SENTINEL') { | |
| throw "Inventory modified a source file" | |
| } | |
| - name: Smoke installed synthetic migration lifecycle | |
| shell: pwsh | |
| run: | | |
| $root = Join-Path $env:RUNNER_TEMP 'migration smoke' | |
| $source = Join-Path $root 'selected source' | |
| $workspace = Join-Path $root 'research workspace' | |
| $env:LOCALAPPDATA = Join-Path $root 'canonical local state' | |
| New-Item -ItemType Directory -Path $source, $workspace | Out-Null | |
| [IO.File]::WriteAllText( | |
| (Join-Path $source 'synthetic-note.md'), | |
| '# SYNTHETIC_MIGRATION_SENTINEL' | |
| ) | |
| [IO.File]::WriteAllText( | |
| (Join-Path $source 'synthetic-paper.pdf'), | |
| '%PDF-1.4 SYNTHETIC' | |
| ) | |
| $planRaw = & .\.demo-venv\Scripts\scriptorium.exe migrate plan ` | |
| --source $source ` | |
| --workspace $workspace ` | |
| --batch-id synthetic-ci ` | |
| --json | |
| if ($LASTEXITCODE -ne 0) { throw "Installed migration plan failed" } | |
| $planSerialized = $planRaw -join "`n" | |
| $plan = $planSerialized | ConvertFrom-Json | |
| if ( | |
| $plan.operation -ne 'plan' -or | |
| $plan.status -ne 'planned' -or | |
| $plan.summary.files -ne 2 | |
| ) { | |
| throw "Installed migration plan report changed" | |
| } | |
| if (Test-Path (Join-Path $workspace 'Sources')) { | |
| throw "Migration plan wrote into the workspace" | |
| } | |
| if (Test-Path $env:LOCALAPPDATA) { | |
| throw "Migration plan persisted private state" | |
| } | |
| $applyRaw = & .\.demo-venv\Scripts\scriptorium.exe migrate apply ` | |
| --source $source ` | |
| --workspace $workspace ` | |
| --batch-id synthetic-ci ` | |
| --json | |
| if ($LASTEXITCODE -ne 0) { throw "Installed migration apply failed" } | |
| $applySerialized = $applyRaw -join "`n" | |
| $apply = $applySerialized | ConvertFrom-Json | |
| if ( | |
| $apply.operation -ne 'apply' -or | |
| $apply.status -ne 'applied' -or | |
| $apply.summary.changed -ne 2 | |
| ) { | |
| throw "Installed migration apply report changed" | |
| } | |
| $verifyRaw = & .\.demo-venv\Scripts\scriptorium.exe migrate verify ` | |
| --workspace $workspace ` | |
| --batch-id synthetic-ci ` | |
| --json | |
| if ($LASTEXITCODE -ne 0) { throw "Installed migration verify failed" } | |
| $verify = ($verifyRaw -join "`n") | ConvertFrom-Json | |
| if ($verify.operation -ne 'verify' -or $verify.status -ne 'applied') { | |
| throw "Installed migration verify report changed" | |
| } | |
| $repeatRaw = & .\.demo-venv\Scripts\scriptorium.exe migrate apply ` | |
| --workspace $workspace ` | |
| --batch-id synthetic-ci ` | |
| --json | |
| if ($LASTEXITCODE -ne 0) { throw "Installed migration reapply failed" } | |
| $repeat = ($repeatRaw -join "`n") | ConvertFrom-Json | |
| if ($repeat.status -ne 'unchanged') { | |
| throw "Installed migration reapply was not idempotent" | |
| } | |
| $rollbackRaw = & .\.demo-venv\Scripts\scriptorium.exe migrate rollback ` | |
| --workspace $workspace ` | |
| --batch-id synthetic-ci ` | |
| --json | |
| if ($LASTEXITCODE -ne 0) { throw "Installed migration rollback failed" } | |
| $rollbackSerialized = $rollbackRaw -join "`n" | |
| $rollback = $rollbackSerialized | ConvertFrom-Json | |
| if ( | |
| $rollback.operation -ne 'rollback' -or | |
| $rollback.status -ne 'rolled-back' -or | |
| $rollback.summary.changed -ne 2 | |
| ) { | |
| throw "Installed migration rollback report changed" | |
| } | |
| $allReports = @( | |
| $planSerialized, | |
| $applySerialized, | |
| ($verifyRaw -join "`n"), | |
| ($repeatRaw -join "`n"), | |
| $rollbackSerialized | |
| ) -join "`n" | |
| foreach ($privateValue in @( | |
| $root, | |
| $source, | |
| $workspace, | |
| 'synthetic-note.md', | |
| 'synthetic-paper.pdf', | |
| 'SYNTHETIC_MIGRATION_SENTINEL' | |
| )) { | |
| if ($allReports.Contains($privateValue)) { | |
| throw "Migration report exposed a private source value" | |
| } | |
| } | |
| if ( | |
| [IO.File]::ReadAllText((Join-Path $source 'synthetic-note.md')) -ne | |
| '# SYNTHETIC_MIGRATION_SENTINEL' | |
| ) { | |
| throw "Migration modified its source" | |
| } | |
| $remaining = @( | |
| Get-ChildItem ` | |
| (Join-Path $workspace 'Sources\Imported\synthetic-ci') ` | |
| -File ` | |
| -Recurse ` | |
| -ErrorAction SilentlyContinue | |
| ) | |
| if ($remaining.Count -ne 0) { | |
| throw "Migration rollback left owned files" | |
| } | |
| - name: Run isolated public pull E2E | |
| shell: pwsh | |
| run: | | |
| & .\.demo-venv\Scripts\python.exe ` | |
| .\scriptorium\tests\e2e_pull.py ` | |
| --provenance-root .\Provenance ` | |
| --spec-root .\scriptorium-spec | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| - name: Run offline Steward to Lectern golden path | |
| shell: pwsh | |
| run: | | |
| & .\.demo-venv\Scripts\python.exe ` | |
| .\scriptorium\tests\e2e_slides.py ` | |
| --steward-root .\steward ` | |
| --spec-root .\scriptorium-spec ` | |
| --lectern-root .\Academic-Slides-Agent | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| - name: Run credential-free golden path | |
| shell: pwsh | |
| run: | | |
| .\.demo-venv\Scripts\scriptorium.exe doctor ` | |
| --target demo ` | |
| --json ` | |
| --spec-root .\scriptorium-spec ` | |
| --steward-root .\steward ` | |
| --provenance-root .\Provenance > .\doctor-report.json | |
| if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } | |
| .\.demo-venv\Scripts\python.exe -c "import json, pathlib; report=json.loads(pathlib.Path(r'.\doctor-report.json').read_text(encoding='utf-8-sig')); assert report['status'] == 'ready'; assert report['readiness']['demo'] == 'ready'; assert report['readiness']['public_alpha'] == 'incomplete'; assert report['readiness']['literature'] in {'detected', 'file-only'}; assert report['readiness']['slides'] != 'ready'; assert report['checks'][0]['network']['os_egress'] == 'not-observed'" | |
| .\.demo-venv\Scripts\scriptorium.exe demo ` | |
| --output '.\demo 输出' ` | |
| --spec-root .\scriptorium-spec ` | |
| --steward-root .\steward ` | |
| --provenance-root .\Provenance | |
| .\.demo-venv\Scripts\python.exe -m json.tool '.\demo 输出\demo-report.json' | |
| .\.demo-venv\Scripts\python.exe -c "import json, pathlib; report=json.loads(pathlib.Path(r'.\demo 输出\demo-report.json').read_text(encoding='utf-8')); assert report['demo_status'] == 'passed'" | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: scriptorium-demo-windows | |
| path: | | |
| demo 输出 | |
| doctor-report.json | |
| install-lifecycle-report.json | |
| retention-days: 14 |