findutils 0.9.0

Rust implementation of GNU findutils
Documentation
on: [push, pull_request]

name: External-testsuites
env:
  CARGO_INCREMENTAL: "0"
jobs:
  gnu-tests:
    permissions:
      actions: read
      contents: write # Publish binaries instead of discarding

    name: Run GNU findutils tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout findutils
        uses: actions/checkout@v6
        with:
          path: findutils
      - name: Checkout GNU findutils
        uses: actions/checkout@v6
        with:
          repository: gnu-mirror-unofficial/findutils
          path: findutils.gnu
          ref: 5768a03ddfb5e18b1682e339d6cdd24ff721c510
          submodules: false

      - name: Override submodule URL and initialize submodules
        # Use github instead of upstream git server
        run: |
          git submodule sync --recursive
          git config submodule.gnulib.url https://github.com/coreutils/gnulib.git
          git submodule update --init --recursive --depth 1
        working-directory: findutils.gnu
      - name: Install dependencies
        shell: bash
        run: |
          # Enable sources & install dependencies
          sudo sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources
          sudo apt-get update
          sudo apt-get build-dep findutils
      - name: Run GNU tests
        shell: bash
        run: |
          cd findutils
          bash util/build-gnu.sh ||:
          install --strip -Dm755 target/release/{find,xargs} -t findutils-x86_64-unknown-linux-gnu
          ZSTD_CLEVEL=19 tar --zstd -caf ../findutils-x86_64-unknown-linux-gnu.tar.zst findutils-x86_64-unknown-linux-gnu
      - name: Publish latest commit
        uses: softprops/action-gh-release@v3
        if: github.event_name == 'push' && github.ref == 'refs/heads/main'
        with:
          tag_name: latest-commit
          body: |
            commit: ${{ github.sha }}
          draft: false
          prerelease: true
          files: |
            findutils-x86_64-unknown-linux-gnu.tar.zst
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      - name: Extract testing info
        shell: bash
        run: |

      - name: Upload gnu-test-report
        uses: actions/upload-artifact@v7
        with:
          name: gnu-test-report
          path: |
            findutils.gnu/find/testsuite/*.log
            findutils.gnu/xargs/testsuite/*.log
            findutils.gnu/tests/**/*.log
      - name: Upload gnu-result
        uses: actions/upload-artifact@v7
        with:
          name: gnu-result
          path: gnu-result.json
      - name: Download artifacts (gnu-result and gnu-test-report)
        uses: actions/github-script@v9
        with:
          script: |
            let fs = require('fs');
            fs.mkdirSync('${{ github.workspace }}/dl', { recursive: true });

            async function downloadArtifact(artifactName) {
              // List all artifacts from the workflow run
              let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
                owner: context.repo.owner,
                repo: context.repo.repo,
                run_id: ${{ github.run_id }},
              });

              // Find the specified artifact
              let matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name === artifactName);
              if (!matchArtifact) {
                throw new Error(`Artifact "${artifactName}" not found.`);
              }

              // Download the artifact
              let download = await github.rest.actions.downloadArtifact({
                owner: context.repo.owner,
                repo: context.repo.repo,
                artifact_id: matchArtifact.id,
                archive_format: 'zip',
              });

              // Save the artifact to a file
              fs.writeFileSync(`${{ github.workspace }}/dl/${artifactName}.zip`, Buffer.from(download.data));
            }

            // Download the required artifacts
            await downloadArtifact("gnu-result");
            await downloadArtifact("gnu-test-report");

      - name: Compare failing tests against master
        shell: bash
        run: |
          ./findutils/util/diff-gnu.sh ./dl ./findutils.gnu
      - name: Compare against main results
        shell: bash
        run: |
          unzip dl/gnu-result.zip -d dl/
          unzip dl/gnu-test-report.zip -d dl/
          mv dl/gnu-result.json latest-gnu-result.json
          python findutils/util/compare_gnu_result.py

  bfs-tests:
    name: Run BFS tests
    runs-on: ubuntu-latest
    steps:
      - name: Checkout findutils
        uses: actions/checkout@v6
        with:
          path: findutils
      - name: Checkout BFS
        uses: actions/checkout@v6
        with:
          repository: tavianator/bfs
          path: bfs
          ref: "4.0"
      - name: Install dependencies
        shell: bash
        run: |
          # Enable sources & install dependencies
          sudo sed -i 's/^Types: deb$/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources
          sudo apt-get update
          sudo apt-get build-dep bfs
      - name: Run BFS tests
        shell: bash
        run: |
          cd findutils
          export CARGO_INCREMENTAL=0
          bash util/build-bfs.sh ||:
      - name: Upload bfs-test-report
        uses: actions/upload-artifact@v7
        with:
          name: bfs-test-report
          path: bfs/tests.log
      - name: Upload bfs-result
        uses: actions/upload-artifact@v7
        with:
          name: bfs-result
          path: bfs-result.json
      - name: Download artifacts (gnu-result and bfs-test-report)
        uses: actions/github-script@v9
        with:
          script: |
            let fs = require('fs');
            fs.mkdirSync('${{ github.workspace }}/dl', { recursive: true });

            async function downloadArtifact(artifactName) {
              // List all artifacts from the workflow run
              let artifacts = await github.rest.actions.listWorkflowRunArtifacts({
                owner: context.repo.owner,
                repo: context.repo.repo,
                run_id: ${{ github.run_id }},
              });

              // Find the specified artifact
              let matchArtifact = artifacts.data.artifacts.find((artifact) => artifact.name === artifactName);
              if (!matchArtifact) {
                throw new Error(`Artifact "${artifactName}" not found.`);
              }

              // Download the artifact
              let download = await github.rest.actions.downloadArtifact({
                owner: context.repo.owner,
                repo: context.repo.repo,
                artifact_id: matchArtifact.id,
                archive_format: 'zip',
              });

              // Save the artifact to a file
              fs.writeFileSync(`${{ github.workspace }}/dl/${artifactName}.zip`, Buffer.from(download.data));
            }

            // Download the required artifacts
            await downloadArtifact("bfs-result");
            await downloadArtifact("bfs-test-report");
      - name: Compare failing tests against main
        shell: bash
        run: |
          ./findutils/util/diff-bfs.sh dl/tests.log bfs/tests.log
      - name: Compare against main results
        shell: bash
        run: |
          unzip dl/bfs-result.zip -d dl/
          unzip dl/bfs-test-report.zip -d dl/
          mv dl/bfs-result.json latest-bfs-result.json
          python findutils/util/compare_bfs_result.py

  upload-annotations:
    name: Upload annotations
    runs-on: ubuntu-latest
    needs: [gnu-tests, bfs-tests]
    if: ${{ github.event_name == 'pull_request' }}

    steps:
      - name: List Annotations
        uses: actions/github-script@v9

        with:
          script: |
            let runs = await github.rest.checks.listForRef({
              owner: context.repo.owner,
              repo: context.repo.repo,
              ref: '${{ github.event.pull_request.head.sha }}'
            });

            let names = ['Run GNU findutils tests', 'Run BFS tests'];
            let results = [];
            runs.data.check_runs.filter(check => names.includes(check.name)).forEach(run => results.push(run));

            let annotations = { data: [], pull_request_number: '${{ github.event.number }}' };
            for (let result of results) {
              let run = await github.rest.checks.listAnnotations({
                owner: context.repo.owner,
                repo: context.repo.repo,
                check_run_id: result.id
              });

              run.data.forEach(data => {
                annotations.data.push({
                  run: result.name,
                  annotation: data
                });
              });
            }

            // Remove duplicate items.
            annotations.data = annotations.data.filter((value, index, self) =>
              self.findIndex(v => v.annotation.message === value.annotation.message) === index);

            let fs = require('fs');
            fs.writeFileSync('${{ github.workspace }}/annotations.json', JSON.stringify(annotations));

      - name: Upload annotations
        uses: actions/upload-artifact@v7
        with:
          name: comment
          path: annotations.json