This repository uses a small set of GitHub Actions workflows to cover code quality, security scanning, coverage, deployments, and repo automation.
This repository uses GitHub Environments to protect deployment secrets and ensure audit control over network deployments. Shared CI steps for Stellar testnet and pnpm are provided as reusable workflows (workflow_call) so the main repo, ILN-Smart-Contract, and ILN-Frontend can reuse the same logic.
Every workflow declares an explicit top-level permissions: block. This overrides the
repository/organization default GITHUB_TOKEN scopes and grants each workflow only the
access it actually needs, so a compromised action or dependency cannot escalate beyond the
declared scope.
Policy
- Every workflow file sets a top-level
permissions:block — there are no implicit defaults. New workflows must add one. - Start from
contents: read(orpermissions: {}when the job touches no repo contents, e.g. a pure HTTP probe) and add scopes only where a step demonstrably needs them. - Prefer a read-only top-level default and elevate at the job level for the single
job that needs more (release/publish jobs do this for
contents: write/id-token: write). This keeps the elevated scope off every other job in the file. - Grant
pull-requests: writeonly where a bot comments on or opens PRs;issues: writeonly where a bot writes issues;security-events: writeonly on the CodeQL analyze jobs;pages: write+id-token: writeonly for the Pages deploy.
Scope-by-workflow summary
| Scope | Workflows |
|---|---|
contents: read only |
ci (top-level default), codeql (top-level), coverage, e2e, e2e-nightly, knip, pr-title-lint, snyk, sdk-browser-tests, sdk-e2e-local-node, docs-deploy (default), deploy (default), reusable-cache-pnpm, reusable-stellar-setup; read-only top-level default on release, sdk-release, scripts-release |
permissions: {} (none) |
reusable-testnet-health (HTTP probe only) |
contents: write |
cli-docs, docs-changelog (commit regenerated docs) |
contents: write + pull-requests: write |
mainnet-checklist-status (open checklist PR) |
contents: write + actions: write |
coordinate-release (tag sibling repos, trigger their workflows) |
contents: read + pull-requests: write |
ci, changeset-check (comment status on PRs) |
security-events: write (job-level) |
codeql analyze jobs (upload results) |
pages: write + id-token: write (job-level) |
docs-deploy (OIDC Pages deploy) |
contents: write + id-token: write (job-level) |
release, sdk-release, scripts-release publish jobs (GitHub Release + npm provenance) |
issues: write |
project-board (move cards; falls back to PROJECT_PAT) |
Reusable workflows (workflow_call) also declare permissions: — the effective token is
still capped by whatever the caller grants, so the declaration documents intent and acts
as a ceiling, never an escalation.
Every third-party action (anything not published by GitHub under actions/*) is pinned
to a full 40-character commit SHA, with the human-readable version in a trailing comment:
- uses: dorny/paths-filter@d1c1ffe0248fe513906c8e24db8ea791d46f8590 # v3.0.3Why: a mutable tag like @v3 can be force-moved to point at malicious code after we
adopt it; a commit SHA is immutable, so a review of that exact revision stays valid. First-
party actions/* actions are trusted and left on major-version tags.
Currently pinned third-party actions
| Action | Pinned version |
|---|---|
pnpm/action-setup |
v4.4.0 |
dorny/paths-filter |
v3.0.3 |
dorny/test-reporter |
v2.7.0 |
codecov/codecov-action |
v5.5.5 |
github/codeql-action (init + analyze) |
v3.37.3 |
taiki-e/install-action |
v2.85.1 (with tool: cargo-llvm-cov) |
softprops/action-gh-release |
v2.6.2 |
changesets/action |
v1.9.0 |
peter-evans/create-pull-request |
v6.1.0 |
dtolnay/rust-toolchain |
stable branch @ 2c7215f (with explicit toolchain: stable) |
upptime/uptime-monitor |
master @ c540f23 |
snyk/actions/setup |
master @ 8e119fb |
Two actions select their behaviour from the ref name, so pinning to a SHA requires passing
that choice as an input instead: dtolnay/rust-toolchain gets toolchain: stable and
taiki-e/install-action gets tool: cargo-llvm-cov.
Keeping SHAs current: Renovate manages these automatically — renovate.json sets
pinDigests: true for the github-actions manager, so Renovate opens PRs that bump both the
SHA and the version comment when a new release ships. Do not hand-edit SHAs to "latest"; let
Renovate propose the update so the version comment stays accurate. See
CONTRIBUTING.md for the contributor policy.
| Workflow | File | Purpose |
|---|---|---|
| Stellar setup | .github/workflows/reusable-stellar-setup.yml |
Install Stellar CLI, configure testnet, create a funded identity |
| pnpm cache | .github/workflows/reusable-cache-pnpm.yml |
Warm the pnpm store with lockfile-scoped cache keys |
| Testnet health | .github/workflows/reusable-testnet-health.yml |
Probe Horizon testnet and expose a healthy flag |
Installs the Stellar CLI via cargo install, registers the public testnet endpoints, and generates a funded identity (Friendbot on testnet).
Inputs
| Input | Default | Description |
|---|---|---|
identity-name |
ci-test |
CLI identity alias |
network |
testnet |
Network alias (stellar network use) |
fund-identity |
true |
Fund via Friendbot when true |
horizon-url |
https://horizon-testnet.stellar.org |
Used for custom testnet registration |
rpc-url |
https://soroban-testnet.stellar.org |
Soroban RPC for testnet registration |
network-passphrase |
Test SDF Network ; September 2015 |
Testnet passphrase |
stellar-cli-features |
opt |
Cargo features for stellar-cli |
Outputs
| Output | Description |
|---|---|
identity-name |
Identity alias written to the CLI config |
public-key |
G… address for the identity |
network |
Network alias in use |
Example (same repository)
jobs:
stellar-setup:
uses: ./.github/workflows/reusable-stellar-setup.yml
with:
identity-name: ci-deployer
deploy-contract:
needs: stellar-setup
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Re-install CLI in this job (cached via cargo) or colocate deploy steps in one job.
- run: echo "Deployer=${{ needs.stellar-setup.outputs.public-key }}"Example (sibling repository)
jobs:
stellar-setup:
uses: Invoice-Liquidity-Network/Invoice-Liquidity-Network/.github/workflows/reusable-stellar-setup.yml@main
with:
identity-name: contract-ciNote: Reusable workflows run in an isolated job. The Stellar CLI binary is not available on other runners unless you install it again (cargo install is fast when the cache hits) or keep deploy/test steps in the same callable workflow.
Checks out the repo, restores or populates the pnpm store cache, and optionally runs pnpm install. Use it as an early job so later jobs benefit from a warm store when they use the same cache keys.
Inputs
| Input | Default | Description |
|---|---|---|
node-version |
20 |
Node.js version |
pnpm-version |
9 |
pnpm version |
lockfile-path |
pnpm-lock.yaml |
Lockfile hashed into the cache key |
cache-key-prefix |
iln |
Prefix for actions/cache keys |
install-dependencies |
false |
Run pnpm install when true |
install-args |
--frozen-lockfile |
Arguments for pnpm install |
Outputs
| Output | Description |
|---|---|
cache-hit |
true when the primary cache key matched |
Cache key format
{cache-key-prefix}-{runner.os}-pnpm-{hashFiles(lockfile-path)}
Restore key prefix: {cache-key-prefix}-{runner.os}-pnpm-
Example
jobs:
pnpm-cache:
uses: ./.github/workflows/reusable-cache-pnpm.yml
unit-tests:
needs: pnpm-cache
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-pnpm
with:
install-args: --frozen-lockfile
- run: pnpm testThe composite action .github/actions/setup-pnpm uses the same cache keys as the reusable workflow so caller jobs stay in sync without duplicating YAML.
This repo provides two shared setup mechanisms for pnpm:
| Mechanism | File | Runs as | When to use |
|---|---|---|---|
| Composite action | .github/actions/setup-pnpm/action.yml |
A step inside a job | Default choice for most jobs. Use when you need pnpm + Node + store cache inside an existing job. |
| Reusable workflow | .github/workflows/reusable-cache-pnpm.yml |
A separate top-level job | Use when you want to pre-warm the pnpm store in an early job so later jobs in the same workflow hit the cache. The reusable workflow calls the composite action internally. |
Guidelines:
- Start with the composite action (
./.github/actions/setup-pnpm) unless you have a specific reason to use the reusable workflow. - Use the reusable workflow only when you need a dedicated cache-warming job that runs before multiple downstream jobs.
- Do not reimplement pnpm/Node setup inline — always use the shared action or workflow.
- For Stellar CLI setup, use
reusable-stellar-setup.ymlwhen you need a funded testnet identity; otherwise install the CLI inline with cargo caching.
Requests /.well-known/stellar.json from Horizon and sets healthy to true when the response contains a valid network passphrase.
Inputs
| Input | Default | Description |
|---|---|---|
horizon-url |
https://horizon-testnet.stellar.org |
Horizon base URL |
max-attempts |
3 |
Retry count |
retry-delay-seconds |
5 |
Delay between attempts |
Outputs
| Output | Description |
|---|---|
healthy |
true or false (string) |
Example — gate testnet integration
jobs:
testnet-health:
uses: ./.github/workflows/reusable-testnet-health.yml
sdk-testnet-integration:
needs: testnet-health
if: needs.testnet-health.outputs.healthy == 'true'
runs-on: ubuntu-latest
steps:
- run: pnpm test:integration:testnetWhen healthy is false, dependent jobs are skipped and the workflow logs a warning from the health job.
Most PR/push-triggered workflows use concurrency groups to cancel superseded runs, saving CI minutes:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: trueWorkflows with cancel-in-progress: true (safe to cancel — no side effects):
ci.yml, cli-docs.yml, cli-smoke.yml, coverage.yml, e2e.yml, codeql.yml, snyk.yml, knip.yml, pr-title-lint.yml, changeset-check.yml, sdk-api-docs.yml, sdk-browser-tests.yml, sdk-bundle-size.yml
Workflows with cancel-in-progress: false (should not be interrupted):
| Workflow | Reason |
|---|---|
release.yml |
Publishing to npm must complete (uses the short-form concurrency: string, so cancel-in-progress defaults to false) |
sdk-release.yml |
Publishing to npm must complete |
scripts-release.yml |
Publishing to npm must complete |
docs-deploy.yml |
Pages deployment must complete |
e2e-nightly.yml |
Scheduled nightly run — no benefit to cancelling |
sdk-e2e-local-node.yml |
Push-only on main — no concurrent runs expected |
upptime.yml |
Scheduled uptime monitoring — no benefit to cancelling |
Workflows excluded from concurrency groups (no overlapping runs):
deploy.yml, coordinate-release.yml, mainnet-checklist-status.yml, project-board.yml, sync-issues.yml, docs-changelog.yml — these are workflow_dispatch, issue-triggered, or tag-triggered and do not produce redundant runs.
The three reusable workflows (reusable-cache-pnpm.yml, reusable-stellar-setup.yml, reusable-testnet-health.yml) also omit concurrency — they run as workflow_call jobs inside the caller's run and inherit the caller's concurrency context.
The diagram below shows the full topology: every workflow, grouped by the trigger that
fires it, plus the workflow_call edges into the reusable workflows (dotted lines). A
workflow can appear under more than one trigger.
flowchart TD
%% ---- Trigger sources ----
PushMain(["push → main"])
PushTags(["push → tags"])
PR(["pull_request"])
Sched(["schedule (cron)"])
Dispatch(["workflow_dispatch"])
Issues(["issues / PR closed"])
%% ---- Reusable workflows (workflow_call callees) ----
subgraph Reusable["Reusable workflows (workflow_call)"]
RCache["reusable-cache-pnpm.yml"]
RHealth["reusable-testnet-health.yml"]
RStellar["reusable-stellar-setup.yml<br/>(cross-repo; unused locally)"]
end
%% ---- push to main ----
PushMain --> CI["ci.yml"]
PushMain --> Coverage["coverage.yml"]
PushMain --> CodeQL["codeql.yml"]
PushMain --> Snyk["snyk.yml"]
PushMain --> Knip["knip.yml"]
PushMain --> SDKLocal["sdk-e2e-local-node.yml"]
PushMain --> Upptime["upptime.yml"]
PushMain --> Release["release.yml"]
PushMain --> DocsDeploy["docs-deploy.yml"]
PushMain --> DocsChangelog["docs-changelog.yml"]
PushMain --> CLIDocs["cli-docs.yml"]
PushMain --> SDKApiDocs["sdk-api-docs.yml"]
PushMain --> SDKBundle["sdk-bundle-size.yml"]
PushMain --> SDKBrowser["sdk-browser-tests.yml"]
PushMain --> E2E["e2e.yml"]
PushMain --> CLISmoke["cli-smoke.yml"]
%% ---- push tags ----
PushTags --> SDKRelease["sdk-release.yml"]
PushTags --> ScriptsRelease["scripts-release.yml"]
PushTags --> DocsChangelog
%% ---- pull_request ----
PR --> CI
PR --> Coverage
PR --> CodeQL
PR --> Snyk
PR --> Knip
PR --> PRTitle["pr-title-lint.yml"]
PR --> Changeset["changeset-check.yml"]
PR --> CLIDocs
PR --> SDKApiDocs
PR --> SDKBundle
PR --> SDKBrowser
PR --> E2E
PR --> CLISmoke
PR --> SDKRelease
PR --> ScriptsRelease
PR --> ProjectBoard["project-board.yml"]
%% ---- schedule ----
Sched --> CodeQL
Sched --> Snyk
Sched --> E2ENightly["e2e-nightly.yml"]
Sched --> Upptime
%% ---- workflow_dispatch ----
Dispatch --> Deploy["deploy.yml"]
Dispatch --> Coordinate["coordinate-release.yml"]
Dispatch --> Upptime
Dispatch --> Release
Dispatch --> SDKRelease
Dispatch --> ScriptsRelease
Dispatch --> SDKLocal
Dispatch --> DocsChangelog
Dispatch --> DocsDeploy
Dispatch --> Mainnet["mainnet-checklist-status.yml"]
Dispatch --> CLISmoke
%% ---- issues / PR closed ----
Issues --> SyncIssues["sync-issues.yml"]
Issues --> ProjectBoard
Issues --> Mainnet
%% ---- reusable-workflow call edges (workflow_call) ----
CI -. calls .-> RCache
CI -. calls .-> RHealth
CLIDocs -. calls .-> RCache
SDKApiDocs -. calls .-> RCache
SDKBundle -. calls .-> RCache
SDKLocal -. calls .-> RCache
Knip -. calls .-> RCache
Deploy -. calls .-> RHealth
- Workflow-to-workflow
workflow_calldependencies do exist. Six workflows call the shared reusable workflows:reusable-cache-pnpm.ymlis called byci.yml(jobpnpm-cache),cli-docs.yml,sdk-api-docs.yml,sdk-bundle-size.yml,sdk-e2e-local-node.yml, andknip.yml.reusable-testnet-health.ymlis called byci.yml(jobtestnet-health) anddeploy.yml(gating the deploy on testnet health).reusable-stellar-setup.ymlis defined for cross-repo reuse but is not currently called by any workflow in this repository.
- Several workflows share the same trigger source, especially
pushtomain,pull_request, and scheduled cron runs. project-board.ymlandsync-issues.ymlact on repository metadata rather than code changes.deploy.ymlis intentionally manual and protected by GitHub Environments.release.yml,sdk-release.yml, andscripts-release.ymlall supportworkflow_dispatchfor manual re-runs after failed publishes.codeql.ymlruns two parallel jobs:analyze-ts(JavaScript/TypeScript across all workspaces) andanalyze-rust(Rust forbackend/).
- Trigger:
pushtomainand everypull_request. - Reusable workflows: calls
reusable-cache-pnpm.yml(jobpnpm-cache, warms the pnpm store for later jobs) andreusable-testnet-health.yml(jobtestnet-health, gates the SDK testnet-integration job). Achangesjob runsdorny/paths-filterso downstream jobs only execute when the relevant paths change. - Jobs (grouped):
- Rust/contract:
test(cargo test),build(contract forwasm32v1-none),lint(cargo clippy+cargo fmt --check). - Node workspace:
node-tests(pnpm test),node-coverage(JS coverage with an 80% minimum),sdk-types-sync(rebuilds the contract spec and verifies the generatedsdk/src/generated/types.tsis current). - SDK testnet:
sdk-testnet-integration(runs whentestnet-healthreports healthy) withsdk-testnet-skippedas the skipped-path counterpart. - Core package:
core-install→core-format/core-lint/core-type-check→core-test→core-build, summarized bycore-summary. - Repo hygiene:
syncpack,validate-packages,no-foreign-lockfiles,license-compliance. - Reporting:
report-test-resultsandnotify-discord(posts on failure).
- Rust/contract:
- Required secrets: none (Discord notification uses an optional webhook).
- Expected runtime: medium to long. The Rust and SDK type-sync jobs are the slowest parts, so a full run is usually 10 to 30 minutes depending on cache hits.
- Debugging tips:
- Check whether the backend submodule was checked out. This workflow uses
submodules: recursive. - For Rust failures, reproduce with
cargo test,cargo clippy, orcargo fmt --checkin the backend submodule. - For SDK sync failures, run
pnpm generate:typesand compare the generated file. - If PR comments are missing, remember the failure-comment step only runs on pull requests.
- Check whether the backend submodule was checked out. This workflow uses
- Trigger:
pushtomainandpull_request(both scoped topackages/cli/**,sdk/**,packages/shared/**, and the workflow file), plus manualworkflow_dispatch. - Runners: GitHub-hosted matrix —
ubuntu-latest,macos-latest,windows-latest(not the self-hostednamespace-profile-nurscaprofile), so the CLI is exercised on all three OSes. - Jobs:
cli-smoke: builds@iln/cliand its workspace deps, packs the CLI together with its unpublished workspace dependencies (@iln/sdk,@iln/shared), installs the tarballs globally in a clean environment, and runsiln --versionandiln --helpas smoke tests.fail-fast: falseso one OS failing still reports the others.
- Required secrets: none.
- Expected runtime: short to medium. Usually 3 to 8 minutes per OS (Windows is the slowest).
- Debugging tips:
- Because the internal packages are unpublished, all three tarballs must be installed in a
single
npm install -gso the@iln/*versions resolve from the sibling tarballs rather than the registry. - Reproduce locally with the manual fallback procedure in CONTRIBUTING.md.
- A
workspace:*specifier leaking into the installed package usually means the CLI's packaging (orpnpm packversion rewriting) regressed.
- Because the internal packages are unpublished, all three tarballs must be installed in a
single
- Trigger:
pushtomain,pull_requesttargetingmain, and a weekly Sunday schedule at 02:00 UTC. - Jobs:
analyze-ts: initializes CodeQL forjavascript-typescriptwith explicitpathscoveringsdk,cli,indexer,notifications,frontend,docs,packages,examples,scripts,workers,contract-deployer,account-seeder, andtests. Ignoresbackend/, markdown,node_modules,dist, andcoveragedirectories.analyze-rust: initializes CodeQL forrustscoped tobackend/. Checks out withsubmodules: recursiveto ensure the Rust workspace is available.
- Required secrets: none.
- Expected runtime: medium. Usually 10 to 20 minutes per job (they run in parallel). The weekly scan can take longer on a cold runner.
- Debugging tips:
- Look for checkout or language-initialization failures first.
- If CodeQL reports an analysis issue, reproduce locally by focusing on the package that owns the file path.
- Confirm the repository has Actions permissions to upload security events.
- The TypeScript scan covers all pnpm workspaces (
sdk,cli,indexer,notifications,packages/*,docs,examples/*) plusfrontend,workers,contract-deployer,account-seeder, andtests.
- Trigger:
pushtomainand everypull_request. - Jobs:
coverage: runs Rust coverage, SDK coverage, frontend coverage, and notifications coverage, then uploads the merged reports to Codecov.
- Required secrets:
CODECOV_TOKEN.
- Expected runtime: long. This is the heaviest workflow in the repo because it runs coverage across multiple packages and the Rust workspace; 20 to 45 minutes is a realistic range.
- Debugging tips:
- Re-run each coverage command locally in the package that failed to see whether the issue is test logic or a threshold failure.
- Check the generated
lcov.infofiles if the Codecov upload fails. - Verify
CODECOV_TOKENis present in repository secrets and that the slug matches the Codecov project.
- Trigger: manual
workflow_dispatch. - Jobs:
deploy: deploys the contract to the selected network.
- Required secrets:
TESTNET_DEPLOYER_SECRETTESTNET_HORIZON_URLMAINNET_DEPLOYER_SECRETMAINNET_HORIZON_URL
- Expected runtime: short to medium. Dry runs finish quickly; real deployments typically take a few minutes, depending on network responsiveness.
- Debugging tips:
- Make sure the chosen environment exists in GitHub and has the required secrets.
- If deployment fails, verify the Horizon URL and secret key for the selected network.
- Use
dry_run: truefirst when validating a new environment or script change.
- Trigger:
pushandpull_request. - Guard: only runs when the repository variable
RUN_E2Eis set totrue(disabled by default to save CI minutes on every push/PR). - Jobs:
e2e-tests: starts a local Stellar node viadocker compose up -d, waits 15 seconds for RPC, runsnpm install && npm run test:e2eat the repo root.
- Scope: Lightweight, root-level E2E smoke tests only. This workflow does NOT initialize git submodules, does NOT deploy contracts, does NOT install frontend dependencies or Playwright, and does NOT upload test artifacts. It is intended as a fast gate for basic integration health.
- Required secrets: none.
- Expected runtime: short to medium. Usually 5 to 15 minutes.
- Debugging tips:
- Confirm Docker and Docker Compose are available on the runner or local machine.
- If the job appears to hang, inspect the local node logs and the
sleep 15wait window. - Run
docker compose up -dandnpm run test:e2elocally to isolate test failures. - If this workflow is skipped, check that
RUN_E2Eis set totruein repository variables.
- Trigger: scheduled daily at 00:00 UTC.
- Guard: runs unconditionally (no repository variable gate).
- Jobs:
e2e-tests: full-stack E2E — initializes git submodules, starts a local Stellar node, waits for RPC with a health-check loop (up to 5 minutes), deploys contracts and seeds accounts, installs frontend dependencies, installs Playwright browsers, runsnpm run test:e2einsidefrontend/with mock API enabled, and uploads the Playwright report as an artifact.
- Scope: Comprehensive, frontend-integrated E2E. Covers the full user journey including contract deployment, account seeding, and browser-based Playwright tests. This is the authoritative E2E coverage gate.
- Required secrets: none.
- Expected runtime: medium to long. Usually 15 to 30 minutes (Playwright browser install is slow).
- Debugging tips:
- Treat it the same as the regular E2E workflow but note it runs in
frontend/, not the repo root. - If nightly runs fail but PR runs pass, compare environment drift, container image freshness, and Docker availability.
- Check the uploaded
e2e-reportartifact for Playwright traces and screenshots.
- Treat it the same as the regular E2E workflow but note it runs in
| Aspect | e2e.yml (PR/push) |
e2e-nightly.yml (scheduled) |
|---|---|---|
| Trigger | Every push and PR | Daily at 00:00 UTC |
| Gate | Requires RUN_E2E=true |
Always runs |
| Submodules | Not initialized | Initialized |
| Contract deploy | No | Yes (contract-deployer + account-seeder) |
| Frontend tests | No (runs at repo root) | Yes (Playwright in frontend/) |
| Test artifacts | None uploaded | Playwright report uploaded |
| Purpose | Fast integration smoke test | Full user-journey regression |
Why the split exists: PR-time E2E is gated behind RUN_E2E=true and runs a lightweight smoke test to catch obvious breaks without burning 20+ minutes of CI on every PR. The nightly run provides comprehensive coverage including Playwright browser tests, contract deployment, and account seeding — catching regressions that only surface in a full-stack environment.
- Trigger:
pull_requestevents on open, edit, reopen, and synchronize. - Jobs:
lint: installs commitlint and checks the pull request title against Conventional Commits rules.
- Required secrets: none.
- Expected runtime: short. Usually under 1 minute.
- Debugging tips:
- Fix the PR title to match the conventional format used in
commitlint.config.js. - If the action fails during setup, check the Node.js version and whether npm can reach the registry.
- Fix the PR title to match the conventional format used in
- Trigger: issue label events and closed pull request events.
- Jobs:
move-blocked: moves issues labeledblockedinto the Blocked view on the organization project board.
- Required secrets:
PROJECT_PATis optional.- If
PROJECT_PATis not set, the workflow falls back toGITHUB_TOKEN.
- Expected runtime: short. Usually under 1 minute.
- Debugging tips:
- Make sure the organization project exists and the Status field still has a
Blockedoption. - Check whether the issue is actually present on the project board before expecting the move to succeed.
- If the workflow cannot update the board, the token may not have the right project permissions.
- Make sure the organization project exists and the Status field still has a
- Trigger:
pushtomainand manualworkflow_dispatch. - Jobs:
e2e-local: checks out the repo, installs dependencies, starts a local Stellar node fromtests/e2e/docker-compose.yml, waits for Horizon, runs SDK local-node e2e tests, and then tears the stack down.
- Required secrets: none.
- Expected runtime: medium. Usually 10 to 20 minutes.
- Debugging tips:
- Verify the Docker Compose file still matches the ports the tests expect.
- If Horizon never becomes reachable, inspect the container logs and the readiness loop.
- Run
pnpm --filter @invoice-liquidity/sdk test:e2e-localafter starting the Docker stack locally.
- Trigger:
pushtomain,pull_requesttargetingmain, and a weekly Sunday schedule at 03:00 UTC. - Jobs:
snyk: installs dependencies and runssnyk test --all-projects --severity-threshold=high.
- Required secrets:
SNYK_TOKEN.
- Expected runtime: medium. Usually 5 to 15 minutes.
- Debugging tips:
- Re-run
pnpm install --frozen-lockfilelocally before blaming Snyk for dependency drift. - Confirm the token is configured in repository secrets.
- If a package is missing from the scan, check that the workspace is still being picked up by
pnpm installand the Snyk CLI.
- Re-run
- Trigger: issue label events.
- Jobs:
sync-issues: copies issues to theILN-Smart-Contractrepo when they are labeledsync:smart-contractorsync:all, copies them toILN-Frontendwhen they are labeledsync:frontendorsync:all, and comments back with the sync destinations.
- Required secrets:
GITHUB_TOKEN.
- Expected runtime: short. Usually under 1 minute.
- Debugging tips:
- Confirm the label names exactly match
sync:smart-contract,sync:frontend, orsync:all. - Check that the target repos exist and are owned by the same organization.
- If duplicates are created, remove and reapply the label only after confirming whether the target issue already exists.
- Confirm the label names exactly match
- Trigger:
pushtomain, a five-minute schedule, and manualworkflow_dispatch. - Jobs:
summary: updates the Upptime summary page.response-time: records response time checks and can send Slack notifications.graphs: refreshes monitoring graphs.static-site: rebuilds the static status site.
- Required secrets:
GITHUB_TOKEN.NOTIFICATION_SLACKfor theresponse-timejob if Slack notifications are enabled.
- Expected runtime: short to medium. Usually 2 to 10 minutes, but the frequent schedule means it may queue often.
- Debugging tips:
- Check the upstream Upptime action logs first, since most failures come from generated file updates or repo permissions.
- If the response-time job fails, verify the Slack webhook secret and the monitored endpoint availability.
- Because it runs every five minutes, transient failures can be caused by overlapping runs or rate limits.
These commands mirror the checks used in CI as closely as practical:
git submodule update --init --recursive
pnpm install --frozen-lockfileRust backend checks:
cd backend
cargo test --target x86_64-unknown-linux-gnu
cargo build --target wasm32v1-none --release
cargo clippy
cargo fmt --checkJavaScript and TypeScript checks from the repo root:
pnpm test
pnpm test:coverage
pnpm generate:typesPackage-specific coverage and test runs:
cd sdk
pnpm test:coverage
pnpm test:e2e-local
cd frontend
pnpm test -- --coverage
cd notifications
pnpm test -- --coverageEnd-to-end local node flow:
docker compose -f tests/e2e/docker-compose.yml up -dThe e2e.yml workflow currently invokes npm run test:e2e after bringing up the local node. If that script is missing in your checkout, treat that as a workflow/package mismatch and inspect the package that is supposed to own the command before relying on the test result.
For the SDK local-node suite used by sdk-e2e-local-node.yml, run:
pnpm --filter @invoice-liquidity/sdk test:e2e-local
docker compose -f tests/e2e/docker-compose.yml downPR title lint:
echo "docs: update ci cd reference" > /tmp/pr-title
npx commitlint --config commitlint.config.js --edit /tmp/pr-titleSecurity scans and code scanning are harder to reproduce exactly outside GitHub Actions, but you can still validate the project with the same package installs and test commands above before pushing.
- Open the failed job and identify the first failing step, not the last one.
- Re-run the equivalent command locally in the package that owns the failure.
- Check repository secrets and environment protection if the job touches deployment, Snyk, or Codecov.
- Confirm the backend submodule is initialized if any Rust step fails unexpectedly.
- For issues that only happen on
main, compare the branch history against the PR branch for lockfile or generated-file drift.
- Environment settings cannot be committed to the repository; they must be created in the GitHub UI.
- If
mainnetis selected, GitHub will block the deployment until required reviewers approve the workflow run. - Sibling repos should pin reusable workflow refs (
@mainor a release tag) rather than floating branches in production CI.
Turborepo optimizes and speeds up the build process for this monorepo by caching task outputs and running tasks in parallel.
The 4 pipelines: build, test, lint, type-check.
How to enable remote caching with Vercel: run pnpm dlx turbo login then pnpm dlx turbo link.
Expected cache hit rates:
- First run: 0% (cold cache, everything runs)
- Subsequent runs with no changes: ~90-100% (full cache hit)
- Typical CI runs with partial changes: ~60-80%
- After SDK changes: ~20-40% (most packages depend on SDK)
Environment variables needed for CI remote caching: TURBO_TOKEN and TURBO_TEAM.