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
4 changes: 3 additions & 1 deletion .github/workflows/incl_release_build_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
workflow_call:
inputs:
version_override:
description: "Release tag. Defaults to `version` from .genvm-monorepo-root"
description: "Release tag and embedded version. Defaults to `version` from .genvm-monorepo-root"
required: false
type: string
default: ""
Expand Down Expand Up @@ -117,6 +117,7 @@ jobs:
runs_on: ${{ matrix.runs_on }}
artifact_name: ${{ matrix.artifact_name }}
job_json: ${{ matrix.job_json }}
version_override: ${{ needs.plan.outputs.tag }}
secrets: inherit

test:
Expand All @@ -134,4 +135,5 @@ jobs:
with_nix: ${{ matrix.with_nix }}
setup_python: ${{ matrix.setup_python }}
job_json: ${{ matrix.job_json }}
version_override: ${{ needs.plan.outputs.tag }}
secrets: inherit
9 changes: 9 additions & 0 deletions .github/workflows/incl_release_build_test_cell_build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ name: Build one release artifact cell
on:
workflow_call:
inputs:
version_override:
description: release version from the plan
required: true
type: string
name:
required: true
type: string
Expand Down Expand Up @@ -41,6 +45,11 @@ jobs:
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0

- name: Apply release version
env:
VERSION_OVERRIDE: ${{ inputs.version_override }}
run: ./support/ci/run.sh pipeline apply-release-version --version-override "$VERSION_OVERRIDE"

# Guards the "Job:" step below: every key must have a step, every step a key.
- name: Check job keys
env:
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/incl_release_build_test_cell_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ name: Test one release platform cell
on:
workflow_call:
inputs:
version_override:
description: release version from the plan
required: true
type: string
name:
required: true
type: string
Expand Down Expand Up @@ -49,6 +53,11 @@ jobs:
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0

- name: Apply release version
env:
VERSION_OVERRIDE: ${{ inputs.version_override }}
run: ./support/ci/run.sh pipeline apply-release-version --version-override "$VERSION_OVERRIDE"

- name: Check job keys
env:
JOB_JSON: ${{ inputs.job_json }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ on:
version_override:
type: string
required: false
description: "Override release tag (e.g., v1.2.3). Defaults to `version` from .genvm-monorepo-root"
description: "Override release tag and embedded version (e.g., v1.2.3). Defaults to `version` from .genvm-monorepo-root"

defaults:
run:
Expand Down
6 changes: 6 additions & 0 deletions docs/contributing/howto/releasing/release-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ one install root, so a full install is one platform asset plus
`genvm-universal`. The platform asset already carries every active executor
line ([versioning.md](versioning.md))

`version_override` sets both the release tag and the embedded manager version.
The plan passes the resolved version to every build and test cell, which applies
it to `.genvm-monorepo-root` before source setup. Nix reads that file for
`GENVM_PROFILE`, so an override changes the build inputs and cache keys.
Executor versions remain pinned by their own manifests

## Verifying an Asset

The publisher uploads `SHA256SUMS` next to the tarballs and records a keyless
Expand Down
17 changes: 17 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@
withZig = false;
withWasi = true;
};
# Enough to build the integration tests' wasm contracts, nothing else.
release-test-rust = import ./support/rust.nix {
inherit pkgs deps system;
withZig = false;
withWasi = true;
withCross = false;
};
custom-rust-builder = import ./support/compile-rust.nix {
inherit pkgs system deps;
zig = import ./support/zig.nix { inherit pkgs deps system; };
Expand Down Expand Up @@ -752,6 +759,16 @@
packages-0 ++ packages-rust ++ packages-debug-test ++ [ pre-commit-check.config.package ];
shellHook = shell-hook-base;
};
# Integration tests against prebuilt release tarballs: no build or fuzz tooling.
devShells.release-tests = pkgs.mkShell {
packages = packages-0 ++ [
release-test-rust
pkgs.python312Packages.jsonnet
pkgs.python312Packages.aiohttp
pkgs.wabt
];
shellHook = shell-hook-base;
};
devShells.full = pkgs.mkShell {
packages =
packages-0
Expand Down
47 changes: 42 additions & 5 deletions support/ci/pipelines/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import json
import os
import re
import subprocess

import ci_lib
from pipelines.tests import GithubStep, SubStep, step_nix_develop, step_run
Expand Down Expand Up @@ -159,7 +160,7 @@ def _cell_amd64_linux() -> dict:
{'type': 'group-start', 'name': 'smoke test modules startup'},
_smoke_step('timeout'),
{'type': 'group-end'},
*stable_test_steps('.?submodules=1#mock-tests'),
*stable_test_steps('.?submodules=1#release-tests'),
],
runs_on='ubuntu-latest',
)
Expand Down Expand Up @@ -238,6 +239,43 @@ def test_cells() -> list[dict]:
return [_cell_amd64_linux(), _cell_arm64_linux(), _cell_arm64_macos()]


def check_release_version(version: str) -> bool:
ident = r'[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*'
semver = re.fullmatch(rf'v[0-9]+\.[0-9]+\.[0-9]+(-{ident})?(\+{ident})?', version)
if (
not semver
or subprocess.run(['git', 'check-ref-format', f'refs/tags/{version}']).returncode
!= 0
):
ci_lib.github_error(f'invalid version {version!r}')
return False
return True


class ApplyReleaseVersion(ci_lib.Pipeline):
def name(self) -> str:
return 'apply-release-version'

def add_to(self, parser: argparse.ArgumentParser) -> None:
parser.add_argument(
'--version-override',
default='',
help='release version to write to .genvm-monorepo-root; empty leaves it unchanged',
)

def handler(self, args: argparse.Namespace) -> int:
if not args.version_override:
return 0
if not check_release_version(args.version_override):
return 1
path = ci_lib.ROOT_DIR / '.genvm-monorepo-root'
monorepo = json.loads(path.read_text())
if monorepo['version'] != args.version_override:
monorepo['version'] = args.version_override
path.write_text(json.dumps(monorepo, indent=4) + '\n')
return 0


class PlanReleaseMatrix(ci_lib.Pipeline):
"""
Emit the release build/test matrices and the tag they are built at.
Expand All @@ -253,7 +291,7 @@ def add_to(self, parser: argparse.ArgumentParser) -> None:
parser.add_argument(
'--version-override',
default='',
help='release tag; defaults to `version` from .genvm-monorepo-root',
help='release tag and embedded version; defaults to `version` from .genvm-monorepo-root',
)

def handler(self, args: argparse.Namespace) -> int:
Expand All @@ -263,8 +301,7 @@ def handler(self, args: argparse.Namespace) -> int:
# Shape only. Whether the tag is already taken depends on the remote and
# is the publisher's business — a PR-label run plans the same tag every
# time and must not care.
if not re.match(r'^v[0-9]+\.[0-9]+\.[0-9]+', tag):
ci_lib.github_error(f'invalid version {tag!r}')
if not check_release_version(tag):
return 1

builds = build_cells()
Expand Down Expand Up @@ -301,4 +338,4 @@ def handler(self, args: argparse.Namespace) -> int:
return 0


COMMANDS = [PlanReleaseMatrix()]
COMMANDS = [PlanReleaseMatrix(), ApplyReleaseVersion()]
84 changes: 84 additions & 0 deletions support/ci/unit_tests/test_release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import argparse
import json

import pytest

from pipelines import release


@pytest.fixture
def monorepo(tmp_path, monkeypatch):
path = tmp_path / '.genvm-monorepo-root'
path.write_text(
json.dumps(
{
'version': 'v0.6.0-rc2',
'active-versions': ['v0.3', 'v0.2'],
'support-only-versions': ['v0.2'],
'artifacts_dir': 'build/test-artifacts',
},
indent=4,
)
+ '\n'
)
monkeypatch.setattr(release.ci_lib, 'ROOT_DIR', tmp_path)
monkeypatch.setattr(release, 'release_filter_tag', lambda: 'integration & stable')
return path


def run(command, override=None):
parser = argparse.ArgumentParser()
command.add_to(parser)
argv = [] if override is None else ['--version-override', override]
return command.handler(parser.parse_args(argv))


@pytest.mark.parametrize('override', [None, '', 'v0.6.0-rc2'])
def test_apply_unchanged_version_does_not_write(monorepo, override):
before = monorepo.read_bytes()
mtime = monorepo.stat().st_mtime_ns
assert run(release.ApplyReleaseVersion(), override) == 0
assert monorepo.read_bytes() == before
assert monorepo.stat().st_mtime_ns == mtime


@pytest.mark.parametrize('override', ['v1.2.3', 'v0.6.0-rc3', 'v1.2.3+build.4'])
def test_apply_changes_only_version_and_preserves_format(monorepo, override):
before = monorepo.read_text()
assert run(release.ApplyReleaseVersion(), override) == 0
assert monorepo.read_text() == before.replace('v0.6.0-rc2', override)


@pytest.mark.parametrize(
'override',
[
'1.2.3',
'v1.2',
'release',
' v1.2.3',
'v1.2.3 rc',
'v1.2.3;x',
'v1.2.3-rc..1',
'v1.2.3-rc.',
'v1.2.3-x.lock',
],
)
def test_plan_and_apply_reject_invalid_version(
monorepo, tmp_path, monkeypatch, override
):
before = monorepo.read_bytes()
output = tmp_path / 'github-output'
monkeypatch.setenv('GITHUB_OUTPUT', str(output))
assert run(release.PlanReleaseMatrix(), override) == 1
assert run(release.ApplyReleaseVersion(), override) == 1
assert monorepo.read_bytes() == before
assert not output.exists()


@pytest.mark.parametrize('override', ['', 'v1.2.3-rc1'])
def test_plan_tags_with_override(monorepo, tmp_path, monkeypatch, override):
output = tmp_path / 'github-output'
monkeypatch.setenv('GITHUB_OUTPUT', str(output))
assert run(release.PlanReleaseMatrix(), override) == 0
outputs = dict(line.split('=', 1) for line in output.read_text().splitlines())
assert outputs['tag'] == (override or 'v0.6.0-rc2')
25 changes: 15 additions & 10 deletions support/rust.nix
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
withLinters ? false,
withZig ? true,
withWasi ? false,
withCross ? true,
...
}@args:
let
Expand Down Expand Up @@ -44,12 +45,17 @@ let
(simpleComponent manifest.pkg.cargo.target.${systemAsRust})
(simpleComponent manifest.pkg.rustc.target.${systemAsRust})
(simpleComponent manifest.pkg.rust-std.target.${systemAsRust})

# cross compilation
(simpleComponent manifest.pkg.rust-std.target.x86_64-unknown-linux-musl)
(simpleComponent manifest.pkg.rust-std.target.aarch64-unknown-linux-musl)
(simpleComponent manifest.pkg.rust-std.target.aarch64-apple-darwin)
]
++ (
if !withCross then
[ ]
else
[
(simpleComponent manifest.pkg.rust-std.target.x86_64-unknown-linux-musl)
(simpleComponent manifest.pkg.rust-std.target.aarch64-unknown-linux-musl)
(simpleComponent manifest.pkg.rust-std.target.aarch64-apple-darwin)
]
)
++ (
if !withLinters then
[ ]
Expand Down Expand Up @@ -88,9 +94,8 @@ pkgs.stdenvNoCC.mkDerivation rec {
buildInputs = [
pkgs.zlib
pkgs.bash

zig
]
++ pkgs.lib.optionals withZig [ zig ]
Comment thread
coderabbitai[bot] marked this conversation as resolved.
++ pkgs.lib.optionals pkgs.stdenv.hostPlatform.isLinux [
pkgs.glibc
pkgs.gcc.cc.lib
Expand Down Expand Up @@ -140,18 +145,18 @@ pkgs.stdenvNoCC.mkDerivation rec {

installPhase = ''
mkdir -p $out
for i in $(find . -type d -maxdepth 2 -mindepth 1) ;
# Each tarball is `<pkg>/<component>/...`; only the components belong in $out.
for i in $(find . -type d -maxdepth 2 -mindepth 2) ;
do
cp -r "$i/." $out/.
done

ls -l "$out"

cp ${rust-objcopy} $out/bin/rust-objcopy
''
+ (
if withZig then
''
cp ${rust-objcopy} $out/bin/rust-objcopy
wrapProgram $out/bin/cargo \
--set CC_x86_64_unknown_linux_musl zig-cc-amd64-linux \
--set CC_x86_64_unknown_linux_gnu zig-cc-amd64-linux-gnu \
Expand Down
Loading