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: 1 addition & 1 deletion .github/actions/install-staged-config/action.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: install-staged-config
description: >-
Installs what `forge script ./script/Build.sol` staged in `.staged-config/` onto the root files of the same names, then removes the dir. Foundry refuses a cheatcode write to the root `foundry.toml`, so a deploy repo's generated config reaches the root only through here. Fails when nothing was staged but `foundry.toml` carries `# rain-deploy:generated:` blocks, and when the dir is empty. A repo that stages nothing and has no such blocks is a no-op. A repo with `script/build.sh` is skipped: that hook installs the dir.
Installs what `forge script ./script/Build.sol` staged in `.staged-config/` onto the root files of the same names, then removes the dir. Foundry refuses a cheatcode write to the root `foundry.toml`, so a deploy repo's generated config reaches the root only through here. Fails when nothing was staged but `foundry.toml` carries `# rain-deploy:generated:` blocks, and when the dir is empty. A repo that stages nothing and has no such blocks is a no-op.
runs:
using: composite
steps:
Expand Down
16 changes: 10 additions & 6 deletions .github/workflows/rainix-copy-artifacts.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
name: rainix-copy-artifacts
on:
workflow_call:
inputs:
regenerate-command:
description: Run after forge build and CopyArtifacts.sol, outside any devshell, to regenerate committed artifacts the fixed steps do not.
type: string
default: ""
secrets:
CACHIX_AUTH_TOKEN:
required: false
Expand Down Expand Up @@ -68,14 +73,13 @@ jobs:
run: |
set -euo pipefail
nix develop github:rainlanguage/rainix/${{ env.RAINIX_SHA }}#sol-shell -c forge script ./script/CopyArtifacts.sol --ffi | tee -a "$RUNNER_TEMP/rainix-codegen.log"
# Catch-all post-forge regen hook: consumer-supplied. Runs outside any
# nix devshell so the script picks shells per command (subgraph-shell,
# sol-shell, etc.) for whatever derived artifacts it emits.
- name: Regenerate derived artifacts
if: hashFiles('script/build.sh') != ''
if: inputs.regenerate-command != ''
env:
REGENERATE_COMMAND: ${{ inputs.regenerate-command }}
run: |
set -euo pipefail
./script/build.sh | tee -a "$RUNNER_TEMP/rainix-codegen.log"
bash -c "$REGENERATE_COMMAND" | tee -a "$RUNNER_TEMP/rainix-codegen.log"
- name: Assert every declared generated path was written
uses: rainlanguage/rainix/.github/actions/codegen-declaration@main
with:
Expand All @@ -85,6 +89,6 @@ jobs:
- name: Assert committed artifacts match freshly built
run: |
if ! git diff --exit-code; then
echo "::error::Committed artifacts are stale. Regenerate (script/build-meta.sh, script/Build.sol, script/CopyArtifacts.sol, script/build.sh, forge fmt) and commit."
echo "::error::Committed artifacts are stale. Regenerate (script/build-meta.sh, script/Build.sol, script/CopyArtifacts.sol, the regenerate-command input, forge fmt) and commit."
exit 1
fi
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,9 +184,11 @@ single job it runs whichever of these the repo has:
- `.staged-config/` → the root files of the same names, then the dir is removed.
`Build.sol` stages there because foundry refuses a cheatcode write to the root
`foundry.toml`. Fails if the dir is empty, or missing while `foundry.toml`
carries `# rain-deploy:generated:` blocks. Skipped when the repo has
`script/build.sh`, which then owns the install.
carries `# rain-deploy:generated:` blocks. rainix is the only installer.
- `forge build` + `./script/CopyArtifacts.sol --ffi` → committed ABI JSON
- the `regenerate-command` input, when set → whatever else the repo commits
(bindings ABIs, subgraph codegen). Runs after the copy, outside any devshell,
so it picks its own shells.

then `forge fmt` and the `git diff` assert.

Expand Down Expand Up @@ -218,6 +220,8 @@ jobs:
copy-artifacts:
uses: rainlanguage/rainix/.github/workflows/rainix-copy-artifacts.yaml@main
secrets: inherit
with:
regenerate-command: ./script/regenerate-bindings.sh
```

This replaces the former `rainix-build-pointers` reusable — a pointer-only repo
Expand Down
7 changes: 0 additions & 7 deletions rainix-static/src/install_staged_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@ use std::path::Path;

pub(crate) const STAGED_DIR: &str = ".staged-config";

/// A consumer with this hook installs the staging dir itself.
pub(crate) const BUILD_HOOK: &str = "script/build.sh";

/// Foundry refuses a cheatcode write to the root `foundry.toml`, so a generated
/// block marked there can only be refreshed through the staging dir.
pub(crate) const GENERATED_MARKER: &str = "# rain-deploy:generated:";
Expand Down Expand Up @@ -58,10 +55,6 @@ pub(crate) fn install(root: &Path) -> Result<Vec<String>, String> {
}

pub(crate) fn run(root: &Path) {
if root.join(BUILD_HOOK).is_file() {
println!("install-staged-config: {BUILD_HOOK} present, leaving {STAGED_DIR}/ to it");
return;
}
match install(root) {
Err(e) => crate::fail(&format!("install-staged-config: {e}")),
Ok(names) if names.is_empty() => {
Expand Down
7 changes: 3 additions & 4 deletions rainix-static/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,9 @@
// declaring nothing passes.
// install-staged-config [--root <dir>]
// install what `forge script ./script/Build.sol` staged in .staged-config/
// onto the root files of the same names, then remove the dir. Skipped
// when script/build.sh exists. Missing fails when foundry.toml carries
// rain-deploy generated markers; empty always fails. Semantics:
// install_staged_config.rs.
// onto the root files of the same names, then remove the dir. Missing
// fails when foundry.toml carries rain-deploy generated markers; empty
// always fails. Semantics: install_staged_config.rs.
// snapshots-append-only [--base <ref>] [--root <dir>]
// fail unless every existing per-tag deploy-pin snapshot under
// <root>/<tag>/ (default root src/generated, base origin/main) survives
Expand Down
39 changes: 6 additions & 33 deletions test/bats/action/install-staged-config.test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -123,53 +123,26 @@ install() {
[[ "$output" == *"does not exist"* ]]
}

@test "a repo with script/build.sh leaves the staging dir to that hook" {
@test "a repo with script/build.sh has its staged config installed" {
mk_marked_consumer
cp "$work/foundry.toml" "$work/before.toml"
mkdir "$work/script" "$work/.staged-config"
printf '#!/usr/bin/env bash\n' >"$work/script/build.sh"
printf 'NEW FOUNDRY\n' >"$work/.staged-config/foundry.toml"

install

[ "$status" -eq 0 ]
[[ "$output" == *"script/build.sh present"* ]]
cmp "$work/foundry.toml" "$work/before.toml"
[ "$(cat "$work/.staged-config/foundry.toml")" = "NEW FOUNDRY" ]
}

@test "a repo with script/build.sh and nothing staged is left to that hook" {
mk_marked_consumer
mkdir "$work/script"
printf '#!/usr/bin/env bash\n' >"$work/script/build.sh"

install

[ "$status" -eq 0 ]
[[ "$output" == *"script/build.sh present"* ]]
}

@test "a directory named script/build.sh is not a hook" {
mk_marked_consumer
mkdir -p "$work/script/build.sh" "$work/.staged-config"
printf 'NEW FOUNDRY\n' >"$work/.staged-config/foundry.toml"

install

[ "$status" -eq 0 ]
[ "$(cat "$work/foundry.toml")" = "NEW FOUNDRY" ]
[ ! -e "$work/.staged-config" ]
}

@test "a repo with other script hooks but no script/build.sh installs the staged config" {
@test "a repo with script/build.sh and nothing staged still fails" {
mk_marked_consumer
mkdir "$work/script" "$work/.staged-config"
printf '#!/usr/bin/env bash\n' >"$work/script/build-meta.sh"
printf 'NEW FOUNDRY\n' >"$work/.staged-config/foundry.toml"
mkdir "$work/script"
printf '#!/usr/bin/env bash\n' >"$work/script/build.sh"

install

[ "$status" -eq 0 ]
[ "$(cat "$work/foundry.toml")" = "NEW FOUNDRY" ]
[ ! -e "$work/.staged-config" ]
[ "$status" -ne 0 ]
[[ "$output" == *"does not exist"* ]]
}
13 changes: 12 additions & 1 deletion test/bats/workflow/rainix-copy-artifacts.test.bats
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ setup() {

@test "every codegen hook reaches the log — none is left undeclarable" {
local hook
for hook in script/build-meta.sh script/Build.sol script/CopyArtifacts.sol script/build.sh; do
for hook in script/build-meta.sh script/Build.sol script/CopyArtifacts.sol REGENERATE_COMMAND; do
echo "$teed" | grep -qF "$hook"
done
}
Expand Down Expand Up @@ -100,3 +100,14 @@ setup() {
[ "$install" -lt "$forge_build" ]
[ "$install" -lt "$verify" ]
}

@test "derived artifacts regenerate from the declared input, never a file the repo happens to have" {
local step
step="$(yq -r '.jobs.copy-artifacts.steps[] | select(.name == "Regenerate derived artifacts")' "$workflow")"
[ "$(echo "$step" | yq -r '.if')" = "inputs.regenerate-command != ''" ]
# shellcheck disable=SC2016
[ "$(echo "$step" | yq -r '.env.REGENERATE_COMMAND')" = '${{ inputs.regenerate-command }}' ]
[ "$(yq -r '.on.workflow_call.inputs.regenerate-command.default' "$workflow")" = "" ]
[ -z "$(grep -F 'script/build.sh' "$workflow" || true)" ]
[ -z "$(echo "$runs" | grep -F 'inputs.' || true)" ]
}
Loading