diff --git a/.github/actions/get-merge-commit/action.yml b/.github/actions/get-merge-commit/action.yml new file mode 100644 index 000000000000..1505d582efd8 --- /dev/null +++ b/.github/actions/get-merge-commit/action.yml @@ -0,0 +1,66 @@ +name: Get merge commit + +description: 'Checks whether the Pull Request is mergeable and returns two commit hashes: The result of a temporary merge of the head branch into the target branch ("merged"), and the parent of that commit on the target branch ("target"). Handles push events and merge conflicts gracefully.' + +outputs: + mergedSha: + description: "The merge commit SHA" + value: ${{ fromJSON(steps.merged.outputs.result).mergedSha }} + targetSha: + description: "The target commit SHA" + value: ${{ fromJSON(steps.merged.outputs.result).targetSha }} + +runs: + using: composite + steps: + - id: merged + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + if (context.eventName == 'push') return { mergedSha: context.sha } + + for (const retryInterval of [5, 10, 20, 40, 80]) { + console.log("Checking whether the pull request can be merged...") + const prInfo = (await github.rest.pulls.get({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number + })).data + + if (prInfo.state != 'open') throw new Error ("PR is not open anymore.") + + if (prInfo.mergeable == null) { + console.log(`GitHub is still computing whether this PR can be merged, waiting ${retryInterval} seconds before trying again...`) + await new Promise(resolve => setTimeout(resolve, retryInterval * 1000)) + continue + } + + if (prInfo.mergeable) { + console.log("The PR can be merged.") + + const mergedSha = prInfo.merge_commit_sha + const targetSha = (await github.rest.repos.getCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: prInfo.merge_commit_sha + })).data.parents[0].sha + + console.log(`Checking the commits:\nmerged:${mergedSha}\ntarget:${targetSha}`) + + return { mergedSha, targetSha } + } else { + console.log("The PR has a merge conflict.") + + const mergedSha = prInfo.head.sha + const targetSha = (await github.rest.repos.compareCommitsWithBasehead({ + owner: context.repo.owner, + repo: context.repo.repo, + basehead: `${prInfo.base.sha}...${prInfo.head.sha}` + })).data.merge_base_commit.sha + + console.log(`Checking the commits:\nmerged:${mergedSha}\ntarget:${targetSha}`) + + return { mergedSha, targetSha } + } + } + throw new Error("Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com.") diff --git a/.github/workflows/check-format.yml b/.github/workflows/check-format.yml index cdc0176b2671..58c76f97da6b 100644 --- a/.github/workflows/check-format.yml +++ b/.github/workflows/check-format.yml @@ -9,18 +9,20 @@ on: permissions: {} jobs: - get-merge-commit: - uses: ./.github/workflows/get-merge-commit.yml - nixos: name: fmt-check runs-on: ubuntu-24.04-arm - needs: get-merge-commit - if: needs.get-merge-commit.outputs.mergedSha steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.get-merge-commit.outputs.mergedSha }} + sparse-checkout: .github/actions + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ steps.get-merge-commit.outputs.mergedSha }} - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31 with: diff --git a/.github/workflows/check-shell.yml b/.github/workflows/check-shell.yml index c26bea7761bc..01dd64913b6a 100644 --- a/.github/workflows/check-shell.yml +++ b/.github/workflows/check-shell.yml @@ -32,7 +32,14 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: refs/pull/${{ github.event.pull_request.number }}/merge + sparse-checkout: .github/actions + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ steps.get-merge-commit.outputs.mergedSha }} - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31 diff --git a/.github/workflows/codeowners-v2.yml b/.github/workflows/codeowners-v2.yml index 16bfd417cb7f..8d36d9b03a1e 100644 --- a/.github/workflows/codeowners-v2.yml +++ b/.github/workflows/codeowners-v2.yml @@ -37,17 +37,19 @@ env: DRY_MODE: ${{ github.event.pull_request.draft && '1' || '' }} jobs: - get-merge-commit: - if: github.repository_owner == 'NixOS' - uses: ./.github/workflows/get-merge-commit.yml - # Check that code owners is valid check: name: Check runs-on: ubuntu-24.04-arm - needs: get-merge-commit - if: github.repository_owner == 'NixOS' && needs.get-merge-commit.outputs.mergedSha + if: github.repository_owner == 'NixOS' steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + sparse-checkout: .github/actions + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31 - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16 @@ -77,7 +79,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.get-merge-commit.outputs.mergedSha }} + ref: ${{ steps.get-merge-commit.outputs.mergedSha }} path: pr - name: Validate codeowners diff --git a/.github/workflows/eval-aliases.yml b/.github/workflows/eval-aliases.yml index 7f9b658081f1..941ffd378ef7 100644 --- a/.github/workflows/eval-aliases.yml +++ b/.github/workflows/eval-aliases.yml @@ -9,18 +9,21 @@ on: permissions: {} jobs: - get-merge-commit: - uses: ./.github/workflows/get-merge-commit.yml - eval-aliases: name: Eval nixpkgs with aliases enabled runs-on: ubuntu-24.04-arm - needs: [ get-merge-commit ] steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + sparse-checkout: .github/actions + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + - name: Check out the PR at the test merge commit uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.get-merge-commit.outputs.mergedSha }} + ref: ${{ steps.get-merge-commit.outputs.mergedSha }} path: nixpkgs - name: Install Nix diff --git a/.github/workflows/eval.yml b/.github/workflows/eval.yml index ecb763043425..416fe1afbc50 100644 --- a/.github/workflows/eval.yml +++ b/.github/workflows/eval.yml @@ -19,17 +19,36 @@ on: permissions: {} jobs: - get-merge-commit: - uses: ./.github/workflows/get-merge-commit.yml + prepare: + name: Prepare + runs-on: ubuntu-24.04-arm + outputs: + mergedSha: ${{ steps.get-merge-commit.outputs.mergedSha }} + targetSha: ${{ steps.get-merge-commit.outputs.targetSha }} + systems: ${{ steps.systems.outputs.systems }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + sparse-checkout: | + .github/actions + ci/supportedSystems.json + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + + - name: Load supported systems + id: systems + run: | + echo "systems=$(jq -c > "$GITHUB_OUTPUT" outpaths: name: Outpaths runs-on: ubuntu-24.04-arm - needs: [ get-merge-commit ] + needs: [ prepare ] strategy: fail-fast: false matrix: - system: ${{ fromJSON(needs.get-merge-commit.outputs.systems) }} + system: ${{ fromJSON(needs.prepare.outputs.systems) }} steps: - name: Enable swap run: | @@ -41,7 +60,7 @@ jobs: - name: Check out the PR at the test merge commit uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.get-merge-commit.outputs.mergedSha }} + ref: ${{ needs.prepare.outputs.mergedSha }} path: nixpkgs - name: Install Nix @@ -67,7 +86,7 @@ jobs: process: name: Process runs-on: ubuntu-24.04-arm - needs: [ outpaths, get-merge-commit ] + needs: [ prepare, outpaths ] outputs: targetRunId: ${{ steps.targetRunId.outputs.targetRunId }} steps: @@ -80,7 +99,7 @@ jobs: - name: Check out the PR at the test merge commit uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.get-merge-commit.outputs.mergedSha }} + ref: ${{ needs.prepare.outputs.mergedSha }} fetch-depth: 2 path: nixpkgs @@ -102,7 +121,7 @@ jobs: path: prResult/* - name: Get target run id - if: needs.get-merge-commit.outputs.targetSha + if: needs.prepare.outputs.targetSha id: targetRunId run: | # Get the latest eval.yml workflow run for the PR's target commit @@ -131,7 +150,7 @@ jobs: echo "targetRunId=$runId" >> "$GITHUB_OUTPUT" env: REPOSITORY: ${{ github.repository }} - TARGET_SHA: ${{ needs.get-merge-commit.outputs.targetSha }} + TARGET_SHA: ${{ needs.prepare.outputs.targetSha }} GH_TOKEN: ${{ github.token }} - uses: actions/download-artifact@v4 @@ -145,8 +164,8 @@ jobs: - name: Compare against the target branch if: steps.targetRunId.outputs.targetRunId run: | - git -C nixpkgs worktree add ../target ${{ needs.get-merge-commit.outputs.targetSha }} - git -C nixpkgs diff --name-only ${{ needs.get-merge-commit.outputs.targetSha }} \ + git -C nixpkgs worktree add ../target ${{ needs.prepare.outputs.targetSha }} + git -C nixpkgs diff --name-only ${{ needs.prepare.outputs.targetSha }} \ | jq --raw-input --slurp 'split("\n")[:-1]' > touched-files.json # Use the target branch to get accurate maintainer info @@ -172,7 +191,7 @@ jobs: tag: name: Tag runs-on: ubuntu-24.04-arm - needs: [ get-merge-commit, process ] + needs: [ prepare, process ] if: needs.process.outputs.targetRunId permissions: pull-requests: write @@ -204,7 +223,7 @@ jobs: - name: Check out Nixpkgs at the base commit uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.get-merge-commit.outputs.targetSha }} + ref: ${{ needs.prepare.outputs.targetSha }} path: base sparse-checkout: ci diff --git a/.github/workflows/get-merge-commit.yml b/.github/workflows/get-merge-commit.yml deleted file mode 100644 index edbda3e040eb..000000000000 --- a/.github/workflows/get-merge-commit.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Get merge commit - -on: - pull_request: - paths: - - .github/workflows/get-merge-commit.yml - workflow_call: - outputs: - mergedSha: - description: "The merge commit SHA" - value: ${{ jobs.resolve-merge-commit.outputs.mergedSha }} - targetSha: - description: "The target commit SHA" - value: ${{ jobs.resolve-merge-commit.outputs.targetSha }} - systems: - description: "The supported systems" - value: ${{ jobs.resolve-merge-commit.outputs.systems }} - -permissions: {} - -jobs: - resolve-merge-commit: - runs-on: ubuntu-24.04-arm - outputs: - mergedSha: ${{ steps.merged.outputs.mergedSha }} - targetSha: ${{ steps.merged.outputs.targetSha }} - systems: ${{ steps.systems.outputs.systems }} - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - path: base - sparse-checkout: ci - - - name: Check if the PR can be merged and get the test merge commit - id: merged - env: - GH_TOKEN: ${{ github.token }} - GH_EVENT: ${{ github.event_name }} - run: | - case "$GH_EVENT" in - push) - echo "mergedSha=${{ github.sha }}" >> "$GITHUB_OUTPUT" - ;; - pull_request*) - if commits=$(base/ci/get-merge-commit.sh ${{ github.repository }} ${{ github.event.number }}); then - echo -e "Checking the commits:\n$commits" - echo "$commits" >> "$GITHUB_OUTPUT" - else - # Skipping so that no notifications are sent - echo "Skipping the rest..." - fi - ;; - esac - - - name: Load supported systems - id: systems - run: | - echo "systems=$(jq -c > "$GITHUB_OUTPUT" diff --git a/.github/workflows/lib-tests.yml b/.github/workflows/lib-tests.yml index d55a109aa9f5..345d59bb9cc4 100644 --- a/.github/workflows/lib-tests.yml +++ b/.github/workflows/lib-tests.yml @@ -12,18 +12,20 @@ on: permissions: {} jobs: - get-merge-commit: - uses: ./.github/workflows/get-merge-commit.yml - nixpkgs-lib-tests: name: nixpkgs-lib-tests runs-on: ubuntu-24.04 - needs: get-merge-commit - if: needs.get-merge-commit.outputs.mergedSha steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.get-merge-commit.outputs.mergedSha }} + sparse-checkout: .github/actions + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ steps.get-merge-commit.outputs.mergedSha }} - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31 with: diff --git a/.github/workflows/manual-nixos-v2.yml b/.github/workflows/manual-nixos-v2.yml index a8eadf63057e..5d5acdd377e7 100644 --- a/.github/workflows/manual-nixos-v2.yml +++ b/.github/workflows/manual-nixos-v2.yml @@ -34,7 +34,14 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: refs/pull/${{ github.event.pull_request.number }}/merge + sparse-checkout: .github/actions + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ steps.get-merge-commit.outputs.mergedSha }} - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31 with: diff --git a/.github/workflows/manual-nixpkgs-v2.yml b/.github/workflows/manual-nixpkgs-v2.yml index f9a5f8aa30b1..b31a6b4949e1 100644 --- a/.github/workflows/manual-nixpkgs-v2.yml +++ b/.github/workflows/manual-nixpkgs-v2.yml @@ -21,7 +21,14 @@ jobs: steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: refs/pull/${{ github.event.pull_request.number }}/merge + sparse-checkout: .github/actions + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ steps.get-merge-commit.outputs.mergedSha }} - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31 with: diff --git a/.github/workflows/nix-parse-v2.yml b/.github/workflows/nix-parse-v2.yml index 4b670331308e..6ae66e964474 100644 --- a/.github/workflows/nix-parse-v2.yml +++ b/.github/workflows/nix-parse-v2.yml @@ -9,18 +9,21 @@ on: permissions: {} jobs: - get-merge-commit: - uses: ./.github/workflows/get-merge-commit.yml - tests: name: nix-files-parseable-check runs-on: ubuntu-24.04-arm - needs: get-merge-commit - if: "needs.get-merge-commit.outputs.mergedSha && !contains(github.event.pull_request.title, '[skip treewide]')" + if: "!contains(github.event.pull_request.title, '[skip treewide]')" steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.get-merge-commit.outputs.mergedSha }} + sparse-checkout: .github/actions + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ steps.get-merge-commit.outputs.mergedSha }} - uses: cachix/install-nix-action@526118121621777ccd86f79b04685a9319637641 # v31 with: diff --git a/.github/workflows/nixpkgs-vet.yml b/.github/workflows/nixpkgs-vet.yml index 160bc27697e8..761ecbf08f2f 100644 --- a/.github/workflows/nixpkgs-vet.yml +++ b/.github/workflows/nixpkgs-vet.yml @@ -17,21 +17,23 @@ permissions: {} # There is a feature request for suppressing notifications on concurrency-canceled runs: https://github.com/orgs/community/discussions/13015 jobs: - get-merge-commit: - uses: ./.github/workflows/get-merge-commit.yml - check: name: nixpkgs-vet # This needs to be x86_64-linux, because we depend on the tooling being pre-built in the GitHub releases. runs-on: ubuntu-24.04 # This should take 1 minute at most, but let's be generous. The default of 6 hours is definitely too long. timeout-minutes: 10 - needs: get-merge-commit - if: needs.get-merge-commit.outputs.mergedSha steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: - ref: ${{ needs.get-merge-commit.outputs.mergedSha }} + sparse-checkout: .github/actions + - name: Check if the PR can be merged and get the test merge commit + uses: ./.github/actions/get-merge-commit + id: get-merge-commit + + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ steps.get-merge-commit.outputs.mergedSha }} # Fetches the merge commit and its parents fetch-depth: 2 diff --git a/ci/OWNERS b/ci/OWNERS index 5466d0f2d8f0..9e8628d9878c 100644 --- a/ci/OWNERS +++ b/ci/OWNERS @@ -15,6 +15,7 @@ # CI /.github/*_TEMPLATE* @SigmaSquadron +/.github/actions @NixOS/Security @Mic92 @zowoq @infinisil @azuwis @wolfgangwalther /.github/workflows @NixOS/Security @Mic92 @zowoq @infinisil @azuwis @wolfgangwalther /.github/workflows/check-format.yml @infinisil @wolfgangwalther /.github/workflows/codeowners-v2.yml @infinisil @wolfgangwalther diff --git a/ci/README.md b/ci/README.md index 6ef665e8b099..7fcda96e3480 100644 --- a/ci/README.md +++ b/ci/README.md @@ -40,46 +40,3 @@ Why not just build the tooling right from the PRs Nixpkgs version? - Because it makes the CI check very fast, since no Nix builds need to be done, even for mass rebuilds. - Because it improves security, since we don't have to build potentially untrusted code from PRs. The tool only needs a very minimal Nix evaluation at runtime, which can work with [readonly-mode](https://nixos.org/manual/nix/stable/command-ref/opt-common.html#opt-readonly-mode) and [restrict-eval](https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-restrict-eval). - -## `get-merge-commit.sh GITHUB_REPO PR_NUMBER` - -Check whether a PR is mergeable and return the test merge commit as -[computed by GitHub](https://docs.github.com/en/rest/guides/using-the-rest-api-to-interact-with-your-git-database?apiVersion=2022-11-28#checking-mergeability-of-pull-requests) and its parent. - -Arguments: -- `GITHUB_REPO`: The repository of the PR, e.g. `NixOS/nixpkgs` -- `PR_NUMBER`: The PR number, e.g. `1234` - -Exit codes: -- 0: The PR can be merged, the hashes of the test merge commit and the target commit are returned on stdout -- 1: The PR cannot be merged because it's not open anymore -- 2: The PR cannot be merged because it has a merge conflict -- 3: The merge commit isn't being computed, GitHub is likely having internal issues, unknown if the PR is mergeable - -### Usage - -This script is implemented as a reusable GitHub Actions workflow, and can be used as follows: - -```yaml -on: pull_request_target - -# We need a token to query the API, but it doesn't need any special permissions -permissions: {} - -jobs: - get-merge-commit: - # use the relative path of the get-merge-commit workflow yaml here - uses: ./.github/workflows/get-merge-commit.yml - - build: - name: Build - runs-on: ubuntu-24.04 - needs: get-merge-commit - steps: - - uses: actions/checkout@ - # Add this to _all_ subsequent steps to skip them - if: needs.get-merge-commit.outputs.mergedSha - with: - ref: ${{ needs.get-merge-commit.outputs.mergedSha }} - - ... -``` diff --git a/ci/get-merge-commit.sh b/ci/get-merge-commit.sh deleted file mode 100755 index c233f7f91691..000000000000 --- a/ci/get-merge-commit.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/usr/bin/env bash -# See ./README.md for docs - -set -euo pipefail - -log() { - echo "$@" >&2 -} - -if (( $# < 2 )); then - log "Usage: $0 GITHUB_REPO PR_NUMBER" - exit 99 -fi -repo=$1 -prNumber=$2 - -# Retry the API query this many times -retryCount=5 -# Start with 5 seconds, but double every retry -retryInterval=5 - -while true; do - log "Checking whether the pull request can be merged" - prInfo=$(gh api \ - -H "Accept: application/vnd.github+json" \ - -H "X-GitHub-Api-Version: 2022-11-28" \ - "/repos/$repo/pulls/$prNumber") - - # Non-open PRs won't have their mergeability computed no matter what - state=$(jq -r .state <<< "$prInfo") - if [[ "$state" != open ]]; then - log "PR is not open anymore" - exit 1 - fi - - mergeable=$(jq -r .mergeable <<< "$prInfo") - if [[ "$mergeable" == "null" ]]; then - if (( retryCount == 0 )); then - log "Not retrying anymore. It's likely that GitHub is having internal issues: check https://www.githubstatus.com/" - exit 3 - else - (( retryCount -= 1 )) || true - - # null indicates that GitHub is still computing whether it's mergeable - # Wait a couple seconds before trying again - log "GitHub is still computing whether this PR can be merged, waiting $retryInterval seconds before trying again ($retryCount retries left)" - sleep "$retryInterval" - - (( retryInterval *= 2 )) || true - fi - else - break - fi -done - -if [[ "$mergeable" == "true" ]]; then - log "The PR can be merged" - mergedSha="$(jq -r .merge_commit_sha <<< "$prInfo")" - echo "mergedSha=$mergedSha" - targetSha="$(gh api "/repos/$repo/commits/$mergedSha" --jq '.parents[0].sha')" - echo "targetSha=$targetSha" -else - log "The PR has a merge conflict" - exit 2 -fi