diff --git a/.github/actions/nix-install-ephemeral/action.yml b/.github/actions/nix-install-ephemeral/action.yml index 5dbdabe8e..239f7777a 100644 --- a/.github/actions/nix-install-ephemeral/action.yml +++ b/.github/actions/nix-install-ephemeral/action.yml @@ -5,6 +5,14 @@ inputs: description: 'Whether to push build outputs to the Nix binary cache' required: false default: 'false' + enable-sccache-sandbox-path: + description: 'Whether to expose /nix/var/cache/sccache in the Nix sandbox' + required: false + default: 'false' + max-jobs: + description: 'Maximum number of parallel Nix builds' + required: false + default: '' aws-region: description: 'AWS region for the Nix binary cache S3 bucket' required: false @@ -48,4 +56,8 @@ runs: substituters = https://cache.nixos.org https://nix-postgres-artifacts.s3.amazonaws.com trusted-public-keys = nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= ${{ inputs.push-to-cache == 'true' && 'post-build-hook = /etc/nix/upload-to-cache.sh' || '' }} - max-jobs = 4 + ${{ inputs.enable-sccache-sandbox-path == 'true' && 'extra-sandbox-paths = /nix/var/cache/sccache?' || '' }} + ${{ inputs.max-jobs != '' && format('max-jobs = {0}', inputs.max-jobs) || '' }} + ${{ inputs.enable-sccache-sandbox-path == 'true' && 'auto-allocate-uids = true' || '' }} + ${{ inputs.enable-sccache-sandbox-path == 'true' && 'use-cgroups = true' || '' }} + experimental-features = nix-command flakes ${{ inputs.enable-sccache-sandbox-path == 'true' && 'cgroups auto-allocate-uids' || '' }} diff --git a/.github/workflows/nix-build.yml b/.github/workflows/nix-build.yml index d77f86b7c..910214b63 100644 --- a/.github/workflows/nix-build.yml +++ b/.github/workflows/nix-build.yml @@ -14,10 +14,6 @@ permissions: contents: write packages: write -concurrency: - group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - jobs: nix-eval: uses: ./.github/workflows/nix-eval.yml @@ -40,17 +36,31 @@ jobs: - name: Checkout Repo if: ${{ matrix.attr != '' }} uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Mount sccache disk + if: ${{ matrix.attr != '' && matrix.postgresql_version && matrix.runs_on.group != 'self-hosted-runners-nix' }} + uses: useblacksmith/stickydisk@v1 + with: + key: ${{ github.repository }}-sccache-${{ runner.os }}-${{ runner.arch }}-${{ matrix.cache_key }} + path: /nix/var/cache/sccache - name: Install nix (ephemeral) if: ${{ matrix.attr != '' && matrix.runs_on.group != 'self-hosted-runners-nix' }} uses: ./.github/actions/nix-install-ephemeral with: push-to-cache: 'true' + enable-sccache-sandbox-path: ${{ matrix.postgresql_version && 'true' || 'false' }} + max-jobs: ${{ matrix.postgresql_version && '1' || '' }} env: DEV_AWS_ROLE: ${{ secrets.DEV_AWS_ROLE }} NIX_SIGN_SECRET_KEY: ${{ secrets.NIX_SIGN_SECRET_KEY }} - name: Install nix (self-hosted) if: ${{ matrix.attr != '' && matrix.runs_on.group == 'self-hosted-runners-nix' }} uses: ./.github/actions/nix-install-self-hosted + - name: Allow sccache cache write access + if: ${{ matrix.attr != '' && matrix.postgresql_version && matrix.runs_on.group != 'self-hosted-runners-nix' }} + run: | + # With auto-allocate-uids, UID 872415232 (0x34000000) maps to nixbld inside sandbox + sudo chown -R 872415232 /nix/var/cache/sccache + sudo chmod -R 2777 /nix/var/cache/sccache - name: nix build if: ${{ matrix.attr != '' }} shell: bash @@ -71,17 +81,31 @@ jobs: - name: Checkout Repo if: ${{ matrix.attr != '' }} uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Mount sccache disk + if: ${{ matrix.attr != '' && matrix.postgresql_version && matrix.runs_on.group != 'self-hosted-runners-nix' }} + uses: useblacksmith/stickydisk@v1 + with: + key: ${{ github.repository }}-sccache-${{ runner.os }}-${{ runner.arch }}-${{ matrix.cache_key }} + path: /nix/var/cache/sccache - name: Install nix (ephemeral) if: ${{ matrix.attr != '' && matrix.runs_on.group != 'self-hosted-runners-nix' }} uses: ./.github/actions/nix-install-ephemeral with: push-to-cache: 'true' + enable-sccache-sandbox-path: ${{ matrix.postgresql_version && 'true' || 'false' }} + max-jobs: ${{ matrix.postgresql_version && '1' || '' }} env: DEV_AWS_ROLE: ${{ secrets.DEV_AWS_ROLE }} NIX_SIGN_SECRET_KEY: ${{ secrets.NIX_SIGN_SECRET_KEY }} - name: Install nix (self-hosted) if: ${{ matrix.attr != '' && matrix.runs_on.group == 'self-hosted-runners-nix' }} uses: ./.github/actions/nix-install-self-hosted + - name: Allow sccache cache write access + if: ${{ matrix.attr != '' && matrix.postgresql_version && matrix.runs_on.group != 'self-hosted-runners-nix' }} + run: | + # With auto-allocate-uids, UID 872415232 (0x34000000) maps to nixbld inside sandbox + sudo chown -R 872415232 /nix/var/cache/sccache + sudo chmod -R 2777 /nix/var/cache/sccache - name: nix build if: ${{ matrix.attr != '' }} shell: bash @@ -102,9 +126,27 @@ jobs: - name: Checkout Repo if: ${{ matrix.attr != '' }} uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Mount sccache disk + if: ${{ matrix.attr != '' && matrix.postgresql_version }} + uses: useblacksmith/stickydisk@v1 + with: + key: ${{ github.repository }}-sccache-${{ runner.os }}-${{ runner.arch }}-${{ matrix.cache_key }} + path: /nix/var/cache/sccache - name: Install nix if: ${{ matrix.attr != '' }} uses: ./.github/actions/nix-install-self-hosted + - name: Configure sccache for Nix builds + if: ${{ matrix.attr != '' && matrix.postgresql_version }} + run: | + # Ensure sccache directory is writable by the build user + mkdir -p /nix/var/cache/sccache + chmod -R 755 /nix/var/cache/sccache + + # Update Nix configuration to allow access to sccache directory in sandbox + # Create temporary nix.conf with our additions + tee -a /etc/nix/nix.conf > /dev/null < /dev/null < bool: """Check if the package is a postgresql extension package.""" - attrs = pkg["attr"].split(".") - return attrs[-2] == "exts" + return ".exts." in pkg["attr"] # thank you buildbot-nix https://github.com/nix-community/buildbot-nix/blob/985d069a2a45cf4a571a4346107671adc2bd2a16/buildbot_nix/buildbot_nix/build_trigger.py#L297 @@ -247,6 +247,31 @@ def is_kvm_pkg(pkg: NixEvalJobsOutput) -> bool: return "kvm" in pkg.get("requiredSystemFeatures", []) +def clean_package_for_output(pkg: NixEvalJobsOutput) -> GitHubActionPackage: + """Convert nix-eval-jobs output to GitHub Actions matrix package""" + runner = get_runner_for_package(pkg) + if runner is None: + raise ValueError(f"No runner configuration for system: {pkg['system']}") + returned_pkg: GitHubActionPackage = { + "attr": pkg["attr"], + "name": pkg["name"], + "system": pkg["system"], + "runs_on": runner, + } + if is_extension_pkg(pkg): + # Extract PostgreSQL version from attribute path + # e.g., legacyPackages.aarch64-linux.psql_17.exts.wrappers-pkgs.0_5_6 + # or legacyPackages.aarch64-linux.psql_17.exts.pg_graphql + attrs = pkg["attr"].split(".") + exts_idx = attrs.index("exts") + pg_version = attrs[exts_idx - 1].split("_")[-1] + returned_pkg["postgresql_version"] = pg_version + returned_pkg["cache_key"] = f"pg{pg_version}" + else: + returned_pkg["cache_key"] = "shared" + return returned_pkg + + def get_runner_for_package(pkg: NixEvalJobsOutput) -> RunsOnConfig | None: """Determine the appropriate GitHub Actions runner for a package. @@ -286,7 +311,7 @@ def main() -> None: args = parser.parse_args() - max_workers: int = os.cpu_count() or 1 + max_workers: int = int(os.cpu_count() / 2) or 1 cmd = build_nix_eval_command(max_workers, args.flake_outputs) @@ -294,23 +319,6 @@ def main() -> None: packages, warnings_list, errors_list = run_nix_eval_jobs(cmd) gh_action_packages = sort_pkgs_by_closures(packages) - def clean_package_for_output(pkg: NixEvalJobsOutput) -> GitHubActionPackage: - """Convert nix-eval-jobs output to GitHub Actions matrix package""" - runner = get_runner_for_package(pkg) - if runner is None: - raise ValueError(f"No runner configuration for system: {pkg['system']}") - returned_pkg: GitHubActionPackage = { - "attr": pkg["attr"], - "name": pkg["name"], - "system": pkg["system"], - "runs_on": runner, - } - if is_extension_pkg(pkg): - # Extract PostgreSQL version from attribute path - attrs = pkg["attr"].split(".") - returned_pkg["postgresql_version"] = attrs[-3].split("_")[-1] - return returned_pkg - # Group packages by system and type (checks vs packages) packages_by_system: Dict[System, List[GitHubActionPackage]] = defaultdict(list) checks_by_system: Dict[System, List[GitHubActionPackage]] = defaultdict(list) diff --git a/nix/packages/github-matrix/tests/test_github_matrix.py b/nix/packages/github-matrix/tests/test_github_matrix.py index 17ce8132d..2546eb6ee 100644 --- a/nix/packages/github-matrix/tests/test_github_matrix.py +++ b/nix/packages/github-matrix/tests/test_github_matrix.py @@ -4,6 +4,7 @@ from github_matrix import ( NixEvalJobsOutput, + clean_package_for_output, get_runner_for_package, is_extension_pkg, is_kvm_pkg, @@ -260,3 +261,80 @@ def test_dependency_order(self): result = sort_pkgs_by_closures([pkg1, pkg2]) assert result == [pkg1, pkg2] + + +class TestCleanPackageForOutput: + def test_extension_package_simple_path(self): + """Test extension package like pg_graphql with simple attr path""" + pkg: NixEvalJobsOutput = { + "attr": "legacyPackages.aarch64-linux.psql_17.exts.pg_graphql", + "attrPath": [ + "legacyPackages", + "aarch64-linux", + "psql_17", + "exts", + "pg_graphql", + ], + "cacheStatus": "notBuilt", + "drvPath": "/nix/store/test.drv", + "name": "pg_graphql", + "system": "aarch64-linux", + } + result = clean_package_for_output(pkg) + assert result["postgresql_version"] == "17" + assert result["cache_key"] == "pg17" + + def test_extension_package_versioned_path(self): + """Test extension package like wrappers with version suffix in attr path""" + pkg: NixEvalJobsOutput = { + "attr": "legacyPackages.aarch64-linux.psql_17.exts.wrappers-pkgs.0_5_6", + "attrPath": [ + "legacyPackages", + "aarch64-linux", + "psql_17", + "exts", + "wrappers-pkgs", + "0_5_6", + ], + "cacheStatus": "notBuilt", + "drvPath": "/nix/store/test.drv", + "name": "wrappers-0.5.6", + "system": "aarch64-linux", + } + result = clean_package_for_output(pkg) + assert result["postgresql_version"] == "17" + assert result["cache_key"] == "pg17" + + def test_extension_package_pg15(self): + """Test extension package with PostgreSQL 15""" + pkg: NixEvalJobsOutput = { + "attr": "legacyPackages.x86_64-linux.psql_15.exts.pg_cron", + "attrPath": [ + "legacyPackages", + "x86_64-linux", + "psql_15", + "exts", + "pg_cron", + ], + "cacheStatus": "notBuilt", + "drvPath": "/nix/store/test.drv", + "name": "pg_cron", + "system": "x86_64-linux", + } + result = clean_package_for_output(pkg) + assert result["postgresql_version"] == "15" + assert result["cache_key"] == "pg15" + + def test_non_extension_package(self): + """Test non-extension package gets shared cache key""" + pkg: NixEvalJobsOutput = { + "attr": "legacyPackages.x86_64-linux.psql_15", + "attrPath": ["legacyPackages", "x86_64-linux", "psql_15"], + "cacheStatus": "notBuilt", + "drvPath": "/nix/store/test.drv", + "name": "postgresql-15.0", + "system": "x86_64-linux", + } + result = clean_package_for_output(pkg) + assert "postgresql_version" not in result + assert result["cache_key"] == "shared"