Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b9978f911 | ||
|
|
a708e09a72 | ||
|
|
b74925283f | ||
|
|
8baab710c3 | ||
|
|
5ca2636ce3 | ||
|
|
8d28215c1b | ||
|
|
88d188aa03 | ||
|
|
5e5281be03 | ||
|
|
4eda475d5c | ||
|
|
9b388fca34 | ||
|
|
b90c599fac | ||
|
|
d9cd53883c | ||
|
|
3eb27c7831 | ||
|
|
3c7488ba1f | ||
|
|
5122acc896 | ||
|
|
a9a332af1c | ||
|
|
b765a4b0c1 | ||
|
|
508d74fe4a | ||
|
|
c45b2f5516 | ||
|
|
3b846498f4 | ||
|
|
238648ddbf | ||
|
|
d37963afcb | ||
|
|
9ebf4e2ba2 | ||
|
|
9d0d86a8f1 | ||
|
|
7c6cbf9dba | ||
|
|
a65af95b5c | ||
|
|
47cebdd562 | ||
|
|
f6e69e734b | ||
|
|
2dbeaea2e1 | ||
|
|
5cef1c4327 | ||
|
|
4171a7fe61 | ||
|
|
142e70bd13 | ||
|
|
f083fa0634 | ||
|
|
ff28e7d83d | ||
|
|
6c00065099 | ||
|
|
cd2e3cba30 | ||
|
|
7f00ab2f16 | ||
|
|
249a4f4a26 | ||
|
|
5fcb15dba5 | ||
|
|
f783eb975c | ||
|
|
5856b37cea | ||
|
|
84c32f8ef0 | ||
|
|
433635b583 | ||
|
|
c5cbd62f9b | ||
|
|
b94b148638 | ||
|
|
a6c028b782 | ||
|
|
7e7abc9407 | ||
|
|
fef7a40433 | ||
|
|
60243b4fa7 | ||
|
|
b7072cb4a5 | ||
|
|
6215664b2e | ||
|
|
ffe4130654 | ||
|
|
e435b9be6c | ||
|
|
5d9f08af1c | ||
|
|
14b156995e | ||
|
|
187ae761b1 | ||
|
|
b96e0e78d8 | ||
|
|
442c2ed0b9 | ||
|
|
d7a713e3c8 | ||
|
|
0cf23a8e34 | ||
|
|
2724f4ad75 | ||
|
|
ebb321e90e | ||
|
|
17a2fece6f | ||
|
|
d389e51925 | ||
|
|
a2da4c1a3b | ||
|
|
7b204c022b | ||
|
|
af30aa40a4 | ||
|
|
4cd9858bfd | ||
|
|
b4c567e43e | ||
|
|
fc2db1e335 | ||
|
|
35bc81a5cf | ||
|
|
59d24ac8dd | ||
|
|
37df236504 | ||
|
|
2e76232e05 | ||
|
|
b72279afb3 | ||
|
|
08d3454b8c | ||
|
|
17f36e91c1 | ||
|
|
5e2eb59959 | ||
|
|
e3ff6cc268 | ||
|
|
e012c4b5c0 | ||
|
|
59551c580b | ||
|
|
deb64e0ccd | ||
|
|
68b5883b0e | ||
|
|
1b747a66a8 | ||
|
|
7fade42bb5 | ||
|
|
f334e1000c | ||
|
|
ed6869944f | ||
|
|
ad01bedff8 | ||
|
|
34651493c1 | ||
|
|
bf7bed170a | ||
|
|
1469cccfde | ||
|
|
5e9b821d80 | ||
|
|
27da0276ec | ||
|
|
2836cb7257 | ||
|
|
8c09b0f5a5 | ||
|
|
5f85e267db | ||
|
|
1535cfdd9b | ||
|
|
8772c166d9 | ||
|
|
6b6067075b | ||
|
|
dc9be6018c | ||
|
|
b295d213e8 | ||
|
|
d3ca9d27b4 | ||
|
|
16f11456b3 | ||
|
|
81d98b9049 | ||
|
|
25efbc0ab7 | ||
|
|
ad1f3d22dc | ||
|
|
3f3dd1270e | ||
|
|
af7131fdb2 | ||
|
|
97e70434b9 | ||
|
|
19363fa810 | ||
|
|
56e6ef5d2f | ||
|
|
fadc36512d | ||
|
|
61152af62b | ||
|
|
7f16a008c9 | ||
|
|
a6d85d8e42 | ||
|
|
dc91d3709c | ||
|
|
8a3f63db27 | ||
|
|
1bb2f89e8d | ||
|
|
b7df132ba1 | ||
|
|
9f6f052c27 | ||
|
|
569ec711ac | ||
|
|
e075bff043 | ||
|
|
628d320e5b | ||
|
|
d102d0be73 | ||
|
|
01b190c59a | ||
|
|
5ecd6afedb | ||
|
|
81e71bcb49 | ||
|
|
b169b4dfc2 | ||
|
|
049e149ea5 | ||
|
|
91affcf90b | ||
|
|
99c8574fc5 | ||
|
|
b5b4cefb77 | ||
|
|
fee1ff33ae | ||
|
|
22abe5ea6c | ||
|
|
c0c85e65fa | ||
|
|
ab6f9d9810 | ||
|
|
1d04c82eb8 | ||
|
|
cdae35308d | ||
|
|
b926499ed4 | ||
|
|
4be480767c | ||
|
|
aeac7d1e63 | ||
|
|
3c7b8ff4ba | ||
|
|
a177e97cfb | ||
|
|
361c45e6c3 | ||
|
|
1c57aaa8e2 | ||
|
|
1bb2609f16 | ||
|
|
e3684f0029 | ||
|
|
f27060898a | ||
|
|
1dc91b0082 | ||
|
|
c3bbd572e9 | ||
|
|
8d7559b52d | ||
|
|
995e32a70d | ||
|
|
3f26fc2936 | ||
|
|
907d01b847 | ||
|
|
3504109373 | ||
|
|
837f2cd2ea | ||
|
|
0a09d32b00 | ||
|
|
da2715dcb5 | ||
|
|
40925c8b4f | ||
|
|
2eeaa80c3b | ||
|
|
a6207cbebc | ||
|
|
98f7338960 | ||
|
|
003b579967 | ||
|
|
d497e881af | ||
|
|
98c4dab5dd | ||
|
|
8e8c2bf9f7 | ||
|
|
3cf5ea5beb | ||
|
|
1617d32d5d | ||
|
|
b3350a21c4 | ||
|
|
8c46bec97f | ||
|
|
d23df4c400 | ||
|
|
8039955354 | ||
|
|
8a2934e519 | ||
|
|
a0d82d678d | ||
|
|
31de5d5ea0 | ||
|
|
78d3b3acf7 | ||
|
|
d9a1cdb9a2 | ||
|
|
1ccd8d069a | ||
|
|
4fb1046c54 | ||
|
|
b0e7940484 | ||
|
|
89b7a039c9 | ||
|
|
398ee76b79 | ||
|
|
ea5a3e45df | ||
|
|
c5e6348825 | ||
|
|
fc39ff7329 | ||
|
|
f6d26ff00f | ||
|
|
c6479b44ad | ||
|
|
8e3d3985e1 | ||
|
|
1710d9cc56 | ||
|
|
b26f59ab2f | ||
|
|
5d452d4174 | ||
|
|
ff0c1cdd06 | ||
|
|
4e024a9576 | ||
|
|
d89243bdb9 | ||
|
|
270f3fdce8 | ||
|
|
69ddd044c5 | ||
|
|
5ff4a7c0c2 | ||
|
|
ce3e4b33b5 | ||
|
|
35ea457759 | ||
|
|
35b6e84c16 | ||
|
|
faea122f87 | ||
|
|
db58849440 | ||
|
|
94bd9b4f14 | ||
|
|
d0ad8eb95d | ||
|
|
df06ef663d | ||
|
|
e82023054e | ||
|
|
9b7b509268 | ||
|
|
70ce8fe410 | ||
|
|
e16a49bb93 | ||
|
|
a8887f83dd | ||
|
|
9667ebe8c3 | ||
|
|
5ed30bd344 | ||
|
|
1996ac2ae3 | ||
|
|
e40ddb1c99 | ||
|
|
8c840078c5 | ||
|
|
4029ae5aba | ||
|
|
3479e9b580 | ||
|
|
0109b369a6 | ||
|
|
77e1abd18e | ||
|
|
17575017d8 | ||
|
|
902611caf1 | ||
|
|
b5a8444f88 | ||
|
|
200bc978b4 | ||
|
|
d58547195b | ||
|
|
ef7f8ad361 | ||
|
|
19f02d5683 | ||
|
|
d253a5808a | ||
|
|
bf47d12e37 | ||
|
|
0ea19a6fd5 | ||
|
|
55177aeca4 | ||
|
|
c8b9173752 | ||
|
|
3fa2227ebf | ||
|
|
2df1e00d8e | ||
|
|
61c405c549 | ||
|
|
3336f93514 | ||
|
|
569f3557e5 | ||
|
|
9ae154a26d | ||
|
|
2ae5b782c7 | ||
|
|
eddee66b6c | ||
|
|
b3ee5bfa9d |
No files matched your search
@@ -0,0 +1,80 @@
|
||||
name: GnuComment
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["GnuTests"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions: {}
|
||||
jobs:
|
||||
post-comment:
|
||||
permissions:
|
||||
actions: read # to list workflow runs artifacts
|
||||
pull-requests: write # to comment on pr
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
if: >
|
||||
github.event.workflow_run.event == 'pull_request'
|
||||
steps:
|
||||
- name: 'Download artifact'
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
// List all artifacts from GnuTests
|
||||
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
run_id: ${{ github.event.workflow_run.id }},
|
||||
});
|
||||
|
||||
// Download the "comment" artifact, which contains a PR number (NR) and result.txt
|
||||
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
|
||||
return artifact.name == "comment"
|
||||
})[0];
|
||||
|
||||
if (!matchArtifact) {
|
||||
console.log('No comment artifact found');
|
||||
return;
|
||||
}
|
||||
|
||||
var download = await github.rest.actions.downloadArtifact({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
artifact_id: matchArtifact.id,
|
||||
archive_format: 'zip',
|
||||
});
|
||||
var fs = require('fs');
|
||||
fs.writeFileSync('${{ github.workspace }}/comment.zip', Buffer.from(download.data));
|
||||
- run: unzip comment.zip || echo "Failed to unzip comment artifact"
|
||||
|
||||
- name: 'Comment on PR'
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
var fs = require('fs');
|
||||
|
||||
// Check if files exist
|
||||
if (!fs.existsSync('./NR')) {
|
||||
console.log('No NR file found, skipping comment');
|
||||
return;
|
||||
}
|
||||
if (!fs.existsSync('./result.txt')) {
|
||||
console.log('No result.txt file found, skipping comment');
|
||||
return;
|
||||
}
|
||||
|
||||
var issue_number = Number(fs.readFileSync('./NR'));
|
||||
var content = fs.readFileSync('./result.txt');
|
||||
|
||||
if (content.toString().trim().length > 7) { // 7 because we have backquote + \n
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue_number,
|
||||
body: 'GNU sed testsuite comparison:\n```\n' + content + '```'
|
||||
});
|
||||
} else {
|
||||
console.log('Comment content too short, skipping');
|
||||
}
|
||||
@@ -0,0 +1,286 @@
|
||||
name: GnuTests
|
||||
|
||||
# Run GNU sed testsuite against the Rust sed implementation
|
||||
# This workflow extracts and runs tests from the GNU sed testsuite to ensure compatibility
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
permissions:
|
||||
contents: write # Publish sed instead of discarding
|
||||
|
||||
# End the current execution if there is a new changeset in the PR
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
env:
|
||||
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
||||
TEST_FULL_SUMMARY_FILE: 'sed-gnu-full-result.json'
|
||||
|
||||
jobs:
|
||||
native:
|
||||
name: Run GNU sed testsuite
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
#### Get the code, setup cache
|
||||
- name: Checkout code (sed)
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
path: 'sed'
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
components: rustfmt
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: "./sed -> target"
|
||||
- name: Checkout code (GNU sed testsuite)
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
repository: 'mirror/sed'
|
||||
path: 'gnu.sed'
|
||||
ref: 'master'
|
||||
persist-credentials: false
|
||||
|
||||
# Omit installing part of https://github.com/actions/runner-images/tree/main/images/ubuntu
|
||||
|
||||
### Build
|
||||
- name: Build Rust sed binary
|
||||
shell: bash
|
||||
run: |
|
||||
## Build Rust sed binary
|
||||
cd 'sed'
|
||||
cargo build --config=profile.release.strip=true --profile=release #-fast
|
||||
tar -C target/release -cf - sed | zstd -19 -o ../sed-x86_64-unknown-linux-gnu.tar.zst
|
||||
- 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: |
|
||||
sed-x86_64-unknown-linux-gnu.tar.zst
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
### Run tests
|
||||
- name: Run GNU sed testsuite
|
||||
shell: bash
|
||||
run: |
|
||||
## Run GNU sed testsuite using our script
|
||||
cd 'sed'
|
||||
# Set GNU sed source directory (testsuite is at gnu.sed/testsuite/)
|
||||
export GNU_SED_DIR="../gnu.sed"
|
||||
# Run tests with JSON output
|
||||
./util/run-gnu-testsuite.sh --json-output "${{ env.TEST_FULL_SUMMARY_FILE }}" || true
|
||||
|
||||
### Upload artifacts
|
||||
- name: Check for JSON results file
|
||||
shell: bash
|
||||
run: |
|
||||
echo "Checking for JSON results file..."
|
||||
ls -la sed/ || true
|
||||
ls -la sed/${{ env.TEST_FULL_SUMMARY_FILE }} || echo "JSON file not found at sed/${{ env.TEST_FULL_SUMMARY_FILE }}"
|
||||
ls -la ${{ env.TEST_FULL_SUMMARY_FILE }} || echo "JSON file not found at ${{ env.TEST_FULL_SUMMARY_FILE }}"
|
||||
find . -name "*json*" -type f || echo "No JSON files found"
|
||||
|
||||
- name: Upload full json results
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: sed-gnu-full-result
|
||||
path: sed/${{ env.TEST_FULL_SUMMARY_FILE }}
|
||||
if-no-files-found: warn
|
||||
|
||||
- name: Upload test logs
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: test-logs
|
||||
path: |
|
||||
sed/test-logs/*.log
|
||||
sed/test-results/*.json
|
||||
|
||||
aggregate:
|
||||
needs: [native]
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
pull-requests: read
|
||||
name: Aggregate GNU test results
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
run: |
|
||||
## VARs setup
|
||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
||||
|
||||
TEST_SUMMARY_FILE='sed-gnu-result.json'
|
||||
outputs TEST_SUMMARY_FILE
|
||||
|
||||
- name: Checkout code (sed)
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
path: 'sed'
|
||||
persist-credentials: false
|
||||
|
||||
- name: Retrieve reference artifacts
|
||||
uses: dawidd6/action-download-artifact@v21
|
||||
continue-on-error: true
|
||||
with:
|
||||
workflow: GnuTests.yml
|
||||
branch: "${{ env.DEFAULT_BRANCH }}"
|
||||
workflow_conclusion: completed
|
||||
path: "reference"
|
||||
if_no_artifact_found: warn
|
||||
|
||||
- name: Download full json results
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: sed-gnu-full-result
|
||||
path: results
|
||||
|
||||
- name: Extract/summarize testing info
|
||||
id: summary
|
||||
shell: bash
|
||||
run: |
|
||||
## Extract/summarize testing info
|
||||
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; }
|
||||
|
||||
# Check if results directory exists and has JSON files
|
||||
json_count=0
|
||||
if [[ -d "results" ]]; then
|
||||
json_count=$(find results -name "*.json" | wc -l)
|
||||
fi
|
||||
|
||||
if [[ "$json_count" -lt 1 ]]; then
|
||||
echo "::error ::Failed to download results json files; failing early"
|
||||
echo "::error ::Contents of results directory:"
|
||||
ls -lR results || echo "::error ::Results directory does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract summary from JSON results
|
||||
RESULT_FILE="results/${{ env.TEST_FULL_SUMMARY_FILE }}"
|
||||
if [[ -f "$RESULT_FILE" ]]; then
|
||||
TOTAL=$(jq -r '.summary.total // 0' "$RESULT_FILE")
|
||||
PASS=$(jq -r '.summary.passed // 0' "$RESULT_FILE")
|
||||
FAIL=$(jq -r '.summary.failed // 0' "$RESULT_FILE")
|
||||
SKIP=$(jq -r '.summary.skipped // 0' "$RESULT_FILE")
|
||||
ERROR=0 # Our format doesn't distinguish errors from failures
|
||||
else
|
||||
echo "::error ::Result file $RESULT_FILE not found"
|
||||
echo "::error ::Available files in results:"
|
||||
find results -type f || true
|
||||
TOTAL=0; PASS=0; FAIL=0; SKIP=0; ERROR=0
|
||||
fi
|
||||
|
||||
output="GNU sed tests summary = TOTAL: $TOTAL / PASS: $PASS / FAIL: $FAIL / SKIP: $SKIP"
|
||||
echo "${output}"
|
||||
|
||||
if [[ "$FAIL" -gt 0 ]]; then
|
||||
echo "::warning ::${output}"
|
||||
fi
|
||||
|
||||
jq -n \
|
||||
--arg date "$(date --rfc-email)" \
|
||||
--arg sha "$GITHUB_SHA" \
|
||||
--arg total "$TOTAL" \
|
||||
--arg pass "$PASS" \
|
||||
--arg skip "$SKIP" \
|
||||
--arg fail "$FAIL" \
|
||||
--arg error "$ERROR" \
|
||||
'{($date): { sha: $sha, total: $total, pass: $pass, skip: $skip, fail: $fail, error: $error }}' > '${{ steps.vars.outputs.TEST_SUMMARY_FILE }}'
|
||||
|
||||
HASH=$(sha1sum '${{ steps.vars.outputs.TEST_SUMMARY_FILE }}' | cut --delim=" " -f 1)
|
||||
outputs HASH TOTAL PASS FAIL SKIP
|
||||
|
||||
- name: Upload SHA1/ID of 'test-summary'
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: "${{ steps.summary.outputs.HASH }}"
|
||||
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}"
|
||||
|
||||
- name: Upload test results summary
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: test-summary
|
||||
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}"
|
||||
|
||||
- name: Compare test failures VS reference
|
||||
shell: bash
|
||||
run: |
|
||||
## Compare test failures VS reference using JSON files
|
||||
REF_SUMMARY_FILE='reference/sed-gnu-full-result/sed-gnu-full-result.json'
|
||||
CURRENT_SUMMARY_FILE="results/${{ env.TEST_FULL_SUMMARY_FILE }}"
|
||||
REPO_DEFAULT_BRANCH='${{ env.DEFAULT_BRANCH }}'
|
||||
|
||||
# Path to ignore file for intermittent issues
|
||||
IGNORE_INTERMITTENT="sed/.github/workflows/ignore-intermittent.txt"
|
||||
|
||||
# Set up comment directory
|
||||
COMMENT_DIR="reference/comment"
|
||||
mkdir -p ${COMMENT_DIR}
|
||||
echo ${{ github.event.number }} > ${COMMENT_DIR}/NR
|
||||
COMMENT_LOG="${COMMENT_DIR}/result.txt"
|
||||
|
||||
COMPARISON_RESULT=0
|
||||
if test -f "${CURRENT_SUMMARY_FILE}"; then
|
||||
if test -f "${REF_SUMMARY_FILE}"; then
|
||||
echo "Reference summary SHA1/ID: $(sha1sum -- "${REF_SUMMARY_FILE}")"
|
||||
echo "Current summary SHA1/ID: $(sha1sum -- "${CURRENT_SUMMARY_FILE}")"
|
||||
|
||||
python3 sed/util/compare_test_results.py \
|
||||
--ignore-file "${IGNORE_INTERMITTENT}" \
|
||||
--output "${COMMENT_LOG}" \
|
||||
"${CURRENT_SUMMARY_FILE}" "${REF_SUMMARY_FILE}"
|
||||
|
||||
COMPARISON_RESULT=$?
|
||||
else
|
||||
echo "::warning ::Skipping test comparison; no prior reference summary is available at '${REF_SUMMARY_FILE}'."
|
||||
fi
|
||||
else
|
||||
echo "::error ::Failed to find summary of test results (missing '${CURRENT_SUMMARY_FILE}'); failing early"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ${COMPARISON_RESULT} -eq 1 ]; then
|
||||
echo "ONLY_INTERMITTENT=false" >> $GITHUB_ENV
|
||||
echo "::error ::Found new non-intermittent test failures"
|
||||
exit 1
|
||||
else
|
||||
echo "ONLY_INTERMITTENT=true" >> $GITHUB_ENV
|
||||
echo "::notice ::No new test failures detected"
|
||||
fi
|
||||
|
||||
- name: Upload comparison log (for GnuComment workflow)
|
||||
if: success() || failure()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: comment
|
||||
path: reference/comment/
|
||||
|
||||
- name: Report test results
|
||||
if: success() || failure()
|
||||
shell: bash
|
||||
run: |
|
||||
## Report final results
|
||||
echo "::notice ::GNU sed testsuite results:"
|
||||
echo "::notice :: Total tests: ${{ steps.summary.outputs.TOTAL }}"
|
||||
echo "::notice :: Passed: ${{ steps.summary.outputs.PASS }}"
|
||||
echo "::notice :: Failed: ${{ steps.summary.outputs.FAIL }}"
|
||||
echo "::notice :: Skipped: ${{ steps.summary.outputs.SKIP }}"
|
||||
|
||||
if [[ "${{ steps.summary.outputs.FAIL }}" -gt 0 ]]; then
|
||||
PASS_RATE=$(( ${{ steps.summary.outputs.PASS }} * 100 / (${{ steps.summary.outputs.PASS }} + ${{ steps.summary.outputs.FAIL }}) ))
|
||||
echo "::notice :: Pass rate: ${PASS_RATE}%"
|
||||
fi
|
||||
@@ -0,0 +1,17 @@
|
||||
name: Security audit
|
||||
|
||||
# spell-checker:ignore (misc) rustsec
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
jobs:
|
||||
audit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: rustsec/audit-check@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -25,25 +25,20 @@ jobs:
|
||||
benchmark-target:
|
||||
- { package: sed }
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Install system dependencies
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get -y update
|
||||
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
uses: mozilla-actions/sccache-action@v0.0.10
|
||||
|
||||
- name: Install cargo-codspeed
|
||||
shell: bash
|
||||
run: cargo install cargo-codspeed --locked
|
||||
- name: Install tools
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-codspeed
|
||||
|
||||
- name: Build benchmarks for ${{ matrix.benchmark-target.package }}
|
||||
shell: bash
|
||||
|
||||
@@ -13,10 +13,31 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: cargo check
|
||||
|
||||
check_android:
|
||||
name: cargo check (Android ${{ matrix.target }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target: [aarch64-linux-android, armv7-linux-androideabi]
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- uses: android-actions/setup-android@v4
|
||||
- name: Install Android NDK
|
||||
run: sdkmanager "ndk;29.0.14206865"
|
||||
- name: Install cargo-ndk
|
||||
run: cargo install cargo-ndk
|
||||
- name: Check Android target
|
||||
run: cargo ndk --platform 21 --target ${{ matrix.target }} check
|
||||
env:
|
||||
ANDROID_NDK_HOME: ${{ env.ANDROID_SDK_ROOT }}/ndk/29.0.14206865
|
||||
|
||||
test:
|
||||
name: cargo test
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -28,7 +49,7 @@ jobs:
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: cargo test --all
|
||||
|
||||
@@ -47,7 +68,7 @@ jobs:
|
||||
run: |
|
||||
git config --global core.autocrlf false
|
||||
git config --global core.eol lf
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
@@ -109,7 +130,7 @@ jobs:
|
||||
ls -al
|
||||
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
|
||||
- name: Upload coverage results (to Codecov.io)
|
||||
uses: codecov/codecov-action@v5
|
||||
uses: codecov/codecov-action@v7
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: ${{ steps.coverage.outputs.report }}
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
name: cargo fmt --all -- --check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: rustup component add rustfmt
|
||||
- run: cargo fmt --all -- --check
|
||||
@@ -44,14 +44,14 @@ jobs:
|
||||
- { os: macos-latest }
|
||||
- { os: windows-latest }
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: stable
|
||||
components: clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Run sccache-cache
|
||||
uses: mozilla-actions/sccache-action@v0.0.9
|
||||
uses: mozilla-actions/sccache-action@v0.0.10
|
||||
- name: Initialize workflow variables
|
||||
id: vars
|
||||
shell: bash
|
||||
@@ -69,9 +69,8 @@ jobs:
|
||||
run: |
|
||||
## `cargo clippy` lint testing
|
||||
unset fault
|
||||
CLIPPY_FLAGS="-W clippy::default_trait_access -W clippy::manual_string_new -W clippy::cognitive_complexity -W clippy::implicit_clone -W clippy::range-plus-one -W clippy::redundant-clone -W clippy::match_bool -W clippy::semicolon_if_nothing_returned"
|
||||
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
|
||||
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
|
||||
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
|
||||
S=$(cargo clippy --all-targets --workspace -psed -- ${CLIPPY_FLAGS} -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
|
||||
S=$(cargo clippy --all-targets --workspace -psed -- -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
|
||||
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
|
||||
@@ -21,18 +21,20 @@ jobs:
|
||||
name: Build the fuzzers
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- name: Install `cargo-fuzz`
|
||||
run: cargo install cargo-fuzz
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-fuzz
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "cargo-fuzz-cache-key"
|
||||
cache-directories: "fuzz/target"
|
||||
- name: Run `cargo-fuzz build`
|
||||
run: cargo +nightly fuzz build
|
||||
# https://github.com/rust-fuzz/cargo-fuzz/issues/398
|
||||
run: env RUSTC_BOOTSTRAP=1 cargo fuzz build --target $(rustc --print host-tuple)
|
||||
|
||||
fuzz-run:
|
||||
needs: fuzz-build
|
||||
@@ -47,18 +49,19 @@ jobs:
|
||||
- { name: fuzz_sed, should_pass: false }
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- name: Install `cargo-fuzz`
|
||||
run: cargo install cargo-fuzz
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-fuzz
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: "cargo-fuzz-cache-key"
|
||||
cache-directories: "fuzz/target"
|
||||
- name: Restore Cached Corpus
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v6
|
||||
with:
|
||||
key: corpus-cache-${{ matrix.test-target.name }}
|
||||
path: |
|
||||
@@ -70,7 +73,8 @@ jobs:
|
||||
run: |
|
||||
mkdir -p fuzz/stats
|
||||
STATS_FILE="fuzz/stats/${{ matrix.test-target.name }}.txt"
|
||||
cargo +nightly fuzz run ${{ matrix.test-target.name }} -- -max_total_time=${{ env.RUN_FOR }} -timeout=${{ env.RUN_FOR }} -detect_leaks=0 -print_final_stats=1 2>&1 | tee "$STATS_FILE"
|
||||
# https://github.com/rust-fuzz/cargo-fuzz/issues/398
|
||||
env RUSTC_BOOTSTRAP=1 cargo fuzz run --target $(rustc --print host-tuple) ${{ matrix.test-target.name }} -- -max_total_time=${{ env.RUN_FOR }} -timeout=${{ env.RUN_FOR }} -detect_leaks=0 -print_final_stats=1 2>&1 | tee "$STATS_FILE"
|
||||
|
||||
# Extract key stats from the output
|
||||
if grep -q "stat::number_of_executed_units" "$STATS_FILE"; then
|
||||
@@ -140,13 +144,13 @@ jobs:
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
- name: Save Corpus Cache
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v6
|
||||
with:
|
||||
key: corpus-cache-${{ matrix.test-target.name }}
|
||||
path: |
|
||||
fuzz/corpus/${{ matrix.test-target.name }}
|
||||
- name: Upload Stats
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fuzz-stats-${{ matrix.test-target.name }}
|
||||
path: |
|
||||
@@ -159,11 +163,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Download all stats
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
path: fuzz/stats-artifacts
|
||||
pattern: fuzz-stats-*
|
||||
@@ -257,7 +261,7 @@ jobs:
|
||||
run: |
|
||||
cat fuzzing_summary.md
|
||||
- name: Upload Summary
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: fuzzing-summary
|
||||
path: fuzzing_summary.md
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# List of intermittent test names to ignore in result comparisons
|
||||
# Format: one test name per line, lines starting with # are comments
|
||||
#
|
||||
# Add test names that are known to be flaky or environment-dependent
|
||||
# Example:
|
||||
# basic_substitution
|
||||
# line_address_test
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
shell: bash
|
||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.30.3/cargo-dist-installer.sh | sh"
|
||||
- name: Cache dist
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/dist
|
||||
@@ -82,7 +82,7 @@ jobs:
|
||||
cat plan-dist-manifest.json
|
||||
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: artifacts-plan-dist-manifest
|
||||
path: plan-dist-manifest.json
|
||||
@@ -116,7 +116,7 @@ jobs:
|
||||
- name: enable windows longpaths
|
||||
run: |
|
||||
git config --global core.longpaths true
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
run: ${{ matrix.install_dist.run }}
|
||||
# Get the dist-manifest
|
||||
- name: Fetch local artifacts
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
@@ -158,7 +158,7 @@ jobs:
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
|
||||
path: |
|
||||
@@ -175,19 +175,19 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
||||
- name: Fetch local artifacts
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
@@ -205,7 +205,7 @@ jobs:
|
||||
|
||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: artifacts-build-global
|
||||
path: |
|
||||
@@ -225,19 +225,19 @@ jobs:
|
||||
outputs:
|
||||
val: ${{ steps.host.outputs.manifest }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
- name: Install cached dist
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: cargo-dist-cache
|
||||
path: ~/.cargo/bin/
|
||||
- run: chmod +x ~/.cargo/bin/dist
|
||||
# Fetch artifacts from scratch-storage
|
||||
- name: Fetch artifacts
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: target/distrib/
|
||||
@@ -250,14 +250,14 @@ jobs:
|
||||
cat dist-manifest.json
|
||||
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||
- name: "Upload dist-manifest.json"
|
||||
uses: actions/upload-artifact@v6
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
# Overwrite the previous copy
|
||||
name: artifacts-dist-manifest
|
||||
path: dist-manifest.json
|
||||
# Create a GitHub Release while uploading all files to it
|
||||
- name: "Download GitHub Artifacts"
|
||||
uses: actions/download-artifact@v7
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
pattern: artifacts-*
|
||||
path: artifacts
|
||||
@@ -290,7 +290,7 @@ jobs:
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
persist-credentials: false
|
||||
submodules: recursive
|
||||
@@ -0,0 +1,28 @@
|
||||
# spell-checker:ignore wasip
|
||||
name: WASI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# End the current execution if there is a new changeset in the PR.
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
|
||||
|
||||
jobs:
|
||||
test_wasi:
|
||||
name: Tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: wasm32-wasip1
|
||||
- name: check
|
||||
run: cargo check --target wasm32-wasip1
|
||||
@@ -0,0 +1,55 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
exclude: ^tests/fixtures/
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-added-large-files
|
||||
- id: check-executables-have-shebangs
|
||||
- id: check-json
|
||||
exclude: '\.vscode/(cSpell|extensions)\.json' # cSpell.json and extensions.json use comments
|
||||
- id: check-shebang-scripts-are-executable
|
||||
exclude: '.+\.rs' # would be triggered by #![some_attribute]
|
||||
- id: check-symlinks
|
||||
- id: check-toml
|
||||
- id: check-yaml
|
||||
args: [ --allow-multiple-documents ]
|
||||
- id: destroyed-symlinks
|
||||
- id: end-of-file-fixer
|
||||
- id: mixed-line-ending
|
||||
args: [ --fix=lf ]
|
||||
- id: trailing-whitespace
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: rust-linting
|
||||
name: Rust linting
|
||||
description: Run cargo fmt on files included in the commit.
|
||||
entry: cargo +stable fmt --
|
||||
pass_filenames: true
|
||||
types: [file, rust]
|
||||
language: system
|
||||
- id: rust-clippy
|
||||
name: Rust clippy
|
||||
description: Run cargo clippy on files included in the commit.
|
||||
entry: cargo +stable clippy --workspace --all-targets --all-features -- -D warnings
|
||||
pass_filenames: false
|
||||
types: [file, rust]
|
||||
language: system
|
||||
- id: cargo-lock-check
|
||||
name: Cargo.lock sync check
|
||||
description: Ensure Cargo.lock and fuzz/Cargo.lock are up-to-date.
|
||||
entry: bash -c 'for dir in . fuzz; do ( cd "$dir" && cargo fetch --quiet ); done'
|
||||
pass_filenames: false
|
||||
files: 'Cargo\.(toml|lock)$'
|
||||
language: system
|
||||
- id: cspell
|
||||
name: Code spell checker (cspell)
|
||||
description: Run cspell to check for spelling errors (if available).
|
||||
entry: bash -c 'if command -v cspell >/dev/null 2>&1; then cspell --no-must-find-files -- "$@"; else echo "cspell not found, skipping spell check"; exit 0; fi' --
|
||||
pass_filenames: true
|
||||
language: system
|
||||
|
||||
ci:
|
||||
skip: [rust-linting, rust-clippy, cargo-lock-check, cspell]
|
||||
@@ -0,0 +1,32 @@
|
||||
# Contributing to sed
|
||||
|
||||
Hi! Welcome to uutils/sed, and thanks for wanting to contribute!
|
||||
|
||||
This project follows the shared conventions of the [uutils](https://github.com/uutils)
|
||||
organization. Before opening a pull request, please read:
|
||||
|
||||
- Our **[Review Guidelines](https://uutils.github.io/reviews/)** — what we expect
|
||||
from a pull request and how reviews are carried out.
|
||||
- Our community's [CODE_OF_CONDUCT.md](./CODE_OF_CONDUCT.md), if present.
|
||||
|
||||
Finally, feel free to join our [Discord](https://discord.gg/wQVJbvJ)!
|
||||
|
||||
> [!WARNING]
|
||||
> uutils is original code and cannot contain any code from GNU or other
|
||||
> strongly-licensed (GPL/LGPL) implementations. We **cannot** accept changes
|
||||
> based on the GNU source code, and you **must not link** to it either. You may
|
||||
> look at permissively-licensed implementations (MIT/BSD) and read the GNU
|
||||
> *manuals* — never the GNU *source*.
|
||||
|
||||
## In short
|
||||
|
||||
- Discuss non-trivial changes in an issue **before** writing the code.
|
||||
- Keep pull requests **small, self-contained, and descriptively titled**
|
||||
(e.g. `sed: fix ...`).
|
||||
- Make sure CI passes: tests are green, `rustfmt` is satisfied, and there are
|
||||
no `clippy` warnings.
|
||||
- Add tests for new behavior; don't let coverage regress.
|
||||
- Write small, atomic commits annotated with the component you touched.
|
||||
|
||||
See the [Review Guidelines](https://uutils.github.io/reviews/) for the full
|
||||
details.
|
||||
Generated
+611
-367
File diff suppressed because it is too large.
Load diff
+58
-29
@@ -35,36 +35,34 @@ chrono = { version = "0.4.37", default-features = false, features = [
|
||||
] }
|
||||
clap = { version = "4.4", features = ["wrap_help", "cargo"] }
|
||||
clap_complete = "4.5"
|
||||
clap_mangen = "0.2"
|
||||
divan = { package = "codspeed-divan-compat", version = "4.0.5" }
|
||||
fancy-regex = "0.17.0"
|
||||
clap_mangen = "0.3"
|
||||
divan = { package = "codspeed-divan-compat", version = "5.0.0" }
|
||||
fancy-regex = "0.18.0"
|
||||
hex = "0.4"
|
||||
libc = "0.2.153"
|
||||
memchr = "2.7.4"
|
||||
memmap2 = "0.9"
|
||||
once_cell = "1.21.3"
|
||||
phf = "0.13.0"
|
||||
phf_codegen = "0.13.0"
|
||||
phf = "0.14.0"
|
||||
phf_codegen = "0.14.0"
|
||||
predicates = "3.1.3"
|
||||
rand = { version = "0.9", features = ["small_rng"] }
|
||||
rand = { version = "0.10.0" }
|
||||
regex = "1.10.4"
|
||||
sysinfo = "0.37"
|
||||
sha2 = "0.11"
|
||||
sysinfo = "0.38"
|
||||
tempfile = "3.10.1"
|
||||
textwrap = { version = "0.16.1", features = ["terminal_size"] }
|
||||
terminal_size = "0.4.2"
|
||||
uucore = { version = "0.5.0", features = ["libc"] }
|
||||
textwrap = { version = "0.16.1", features = ["terminal_size"] }
|
||||
uucore = { version = "0.9.0", features = ["libc"] }
|
||||
xattr = "1.3.1"
|
||||
|
||||
|
||||
[dependencies]
|
||||
assert_fs = { workspace = true }
|
||||
clap = { workspace = true }
|
||||
clap_complete = { workspace = true }
|
||||
clap_mangen = { workspace = true }
|
||||
ctor = "0.6.0"
|
||||
fancy-regex = { workspace = true }
|
||||
memchr = { workspace = true }
|
||||
memmap2.workspace = true
|
||||
once_cell = { workspace = true }
|
||||
phf = { workspace = true }
|
||||
predicates = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
@@ -75,21 +73,25 @@ textwrap = { workspace = true }
|
||||
uucore = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_fs = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
ctor = "1.0.0"
|
||||
divan = { workspace = true }
|
||||
hex = { workspace = true }
|
||||
libc = { workspace = true }
|
||||
pretty_assertions = "1"
|
||||
rand = { workspace = true }
|
||||
regex = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
uucore = { workspace = true, features = ["entries", "process", "signals", "benchmark"] }
|
||||
uutests = "0.5.0"
|
||||
uutests = "0.9.0"
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
xattr = { workspace = true }
|
||||
|
||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dev-dependencies]
|
||||
rlimit = "0.10.1"
|
||||
rlimit = "0.11.0"
|
||||
|
||||
[build-dependencies]
|
||||
phf_codegen = { workspace = true }
|
||||
@@ -105,26 +107,53 @@ path = "src/bin/sed.rs"
|
||||
name = "sed_bench"
|
||||
harness = false
|
||||
|
||||
# The default release profile. It contains all optimizations, without
|
||||
# sacrificing debug info. With this profile (like in the standard
|
||||
# release profile), the debug info and the stack traces will still be available.
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
||||
# A release-like profile that is tuned to be fast, even when being fast
|
||||
# compromises on binary size. This includes aborting on panic.
|
||||
[profile.release-fast]
|
||||
inherits = "release"
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
# should be dropped to 1 for binary size without performance drop
|
||||
codegen-units = 7
|
||||
|
||||
# A release-like profile that is as small as possible.
|
||||
[profile.release-small]
|
||||
inherits = "release-fast"
|
||||
inherits = "release"
|
||||
opt-level = "z"
|
||||
strip = true
|
||||
|
||||
# The profile that 'cargo dist' will build with
|
||||
[profile.dist]
|
||||
inherits = "release"
|
||||
lto = "thin"
|
||||
[lints.clippy]
|
||||
all = { level = "warn", priority = -1 }
|
||||
|
||||
cargo = { level = "warn", priority = -1 }
|
||||
|
||||
pedantic = { level = "warn", priority = -1 }
|
||||
# The counts were generated with this command:
|
||||
# cargo +nightly clippy --all-targets --workspace --message-format=json --quiet \
|
||||
# | jq -r '.message.code.code | select(. != null and startswith("clippy::"))' \
|
||||
# | sort | uniq -c | sort -h -r
|
||||
missing_errors_doc = "allow" # 69
|
||||
doc_markdown = "allow" # 63
|
||||
must_use_candidate = "allow" # 56
|
||||
needless_raw_string_hashes = "allow" # 20
|
||||
needless_pass_by_value = "allow" # 15
|
||||
missing_panics_doc = "allow" # 12
|
||||
cast_possible_truncation = "allow" # 7
|
||||
unnecessary_wraps = "allow" # 6
|
||||
match_wildcard_for_single_variants = "allow" # 4
|
||||
cast_sign_loss = "allow" # 4
|
||||
cast_possible_wrap = "allow" # 4
|
||||
uninlined_format_args = "allow" # 3
|
||||
similar_names = "allow" # 3
|
||||
used_underscore_binding = "allow" # 2
|
||||
too_many_lines = "allow" # 2
|
||||
struct_excessive_bools = "allow" # 2
|
||||
match_same_arms = "allow" # 2
|
||||
ignore_without_reason = "allow" # 2
|
||||
format_push_string = "allow" # 2
|
||||
should_panic_without_expect = "allow" # 1
|
||||
many_single_char_names = "allow" # 1
|
||||
comparison_chain = "allow"
|
||||
multiple_crate_versions = "allow"
|
||||
unnested_or_patterns = "allow"
|
||||
|
||||
restriction = { level = "allow", priority = -1 }
|
||||
cognitive_complexity = "warn"
|
||||
redundant_clone = "warn"
|
||||
@@ -3,7 +3,7 @@
|
||||
[](https://github.com/uutils/sed/blob/main/LICENSE)
|
||||
[](https://deps.rs/repo/github/uutils/sed)
|
||||
|
||||
[](https://codecov.io/gh/uutils/sed)
|
||||
[](https://codecov.io/gh/uutils/sed)
|
||||
|
||||
# sed
|
||||
|
||||
@@ -14,7 +14,7 @@ and other extensions.
|
||||
|
||||
## Status
|
||||
|
||||
At this state _sed_ implements all POSIX commands
|
||||
At this state _sed_ implements all [POSIX features](https://pubs.opengroup.org/onlinepubs/9799919799/)
|
||||
and can run correctly the two complex scripts of its integration tests:
|
||||
[hanoi.sed](https://github.com/uutils/sed/blob/main/tests/fixtures/sed/script/hanoi.sed) (solves the Towers of Hanoi puzzle) and
|
||||
[math.sed](https://github.com/uutils/sed/blob/main/tests/fixtures/sed/script/math.sed) (implements an arbitrary precision integer math calculator).
|
||||
@@ -28,7 +28,10 @@ Further work aims to:
|
||||
|
||||
## Installation
|
||||
|
||||
Ensure you have Rust installed on your system. You can install Rust through [rustup](https://rustup.rs/).
|
||||
We provide a Linux x86_64 binary archive from the main branch at
|
||||
https://github.com/uutils/sed/releases/tag/latest-commit .
|
||||
|
||||
For other platforms, ensure you have Rust installed on your system. You can install Rust through [rustup](https://rustup.rs/).
|
||||
|
||||
Clone the repository and build the project using Cargo:
|
||||
|
||||
@@ -41,6 +44,37 @@ cargo run --release
|
||||
|
||||
The binary is named `sed` in `target/release/sed`.
|
||||
|
||||
## Testing
|
||||
|
||||
### GNU sed Compatibility Testing
|
||||
|
||||
Test compatibility against GNU sed by running the upstream testsuite shell scripts
|
||||
with a lightweight gnulib test-framework shim:
|
||||
|
||||
```bash
|
||||
# Clone GNU sed testsuite (one time setup)
|
||||
git clone https://github.com/mirror/sed.git ../gnu.sed
|
||||
|
||||
# Run compatibility tests
|
||||
./util/run-gnu-testsuite.sh
|
||||
|
||||
# Verbose mode shows failure details
|
||||
./util/run-gnu-testsuite.sh -v
|
||||
|
||||
# Generate JSON results for CI
|
||||
./util/run-gnu-testsuite.sh --json-output results.json
|
||||
```
|
||||
|
||||
The harness executes each `.sh` test from the GNU sed testsuite directly, injecting
|
||||
our Rust sed binary via `PATH` and providing shim implementations of the gnulib test
|
||||
framework functions (`compare_`, `returns_`, `skip_`, etc.).
|
||||
|
||||
### Unit Tests
|
||||
|
||||
```bash
|
||||
cargo test
|
||||
```
|
||||
|
||||
## Extensions and incompatibilities
|
||||
### Supported GNU extensions
|
||||
* Command-line arguments can be specified in long (`--`) form.
|
||||
@@ -53,11 +87,16 @@ The binary is named `sed` in `target/release/sed`.
|
||||
* The `a`, `c`, and `i` commands do not require an initial backslash,
|
||||
allow text to appear on the same line, and support escape sequences
|
||||
in the specified text.
|
||||
* The `a`, `i`, `=`, `l`, `q` and `r` commands support address range as an extension to POSIX.
|
||||
* The substitution command replacement group `\0` is a synonym for &.
|
||||
* A `Q` command (optionally followed by an exit code) quits immediately.
|
||||
* The `q` command can be optionally followed by an exit code.
|
||||
* The `l` command can be optionally followed by the output width.
|
||||
* The `--follow-symlinks` flag for in-place editing.
|
||||
* Address 0 can be used to specify an address range that is already
|
||||
active on line 1 and can finish with the specified regular expression.
|
||||
* Address steps can be specified in the form of start~step and start,~step
|
||||
ranges.
|
||||
|
||||
### Supported BSD and GNU extensions
|
||||
* The second address in a range can be specified as a relative address with +N.
|
||||
@@ -87,6 +126,13 @@ The binary is named `sed` in `target/release/sed`.
|
||||
* Labels are parsed for alphanumeric characters. The BSD version parses them
|
||||
until the end of the line, preventing ; to be used as a separator.
|
||||
|
||||
## GNU test suite compatibility
|
||||
|
||||
Below is the evolution of how many GNU tests uutils passes.
|
||||
|
||||

|
||||
|
||||
|
||||
## License
|
||||
|
||||
sed is licensed under the MIT License - see the `LICENSE` file for details
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
We provide security updates only for the latest released version of `uutils/sed`.
|
||||
Older versions may not receive patches.
|
||||
If you are using a version packaged by your Linux distribution, please check with your distribution maintainers for their update policy.
|
||||
|
||||
---
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
**Do not open public GitHub issues for security vulnerabilities.**
|
||||
This prevents accidental disclosure before a fix is available.
|
||||
|
||||
Instead, please use the following method:
|
||||
|
||||
- **Email:** [sylvestre@debian.org](mailto:Sylvestre@debian.org)
|
||||
- **Encryption (optional):** You may encrypt your report using our PGP key:
|
||||
Fingerprint: B60D B599 4D39 BEC4 D1A9 5CCF 7E65 28DA 752F 1BE1
|
||||
---
|
||||
|
||||
### What to Include in Your Report
|
||||
|
||||
To help us investigate and resolve the issue quickly, please include as much detail as possible:
|
||||
|
||||
- **Type of issue:** e.g. privilege escalation, information disclosure.
|
||||
- **Location in the source:** file path, commit hash, branch, or tag.
|
||||
- **Steps to reproduce:** exact commands, test cases, or scripts.
|
||||
- **Special configuration:** any flags, environment variables, or system setup required.
|
||||
- **Affected systems:** OS/distribution and version(s) where the issue occurs.
|
||||
- **Impact:** your assessment of the potential severity (DoS, RCE, data leak, etc.).
|
||||
|
||||
---
|
||||
|
||||
## Disclosure Policy
|
||||
|
||||
We follow a **Coordinated Vulnerability Disclosure (CVD)** process:
|
||||
|
||||
1. We will acknowledge receipt of your report within **10 days**.
|
||||
2. We will investigate, reproduce, and assess the issue.
|
||||
3. We will provide a timeline for developing and releasing a fix.
|
||||
4. Once a fix is available, we will publish a GitHub Security Advisory.
|
||||
5. You will be credited in the advisory unless you request anonymity.
|
||||
@@ -17,7 +17,7 @@ fn no_op_short(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let mut data = Vec::new();
|
||||
for i in 0..10_000_000 {
|
||||
data.extend_from_slice(format!("{}\n", i).as_bytes());
|
||||
data.extend_from_slice(format!("{i}\n").as_bytes());
|
||||
}
|
||||
let file_path = create_test_file(&data, temp_dir.path());
|
||||
let file_path_str = file_path.to_str().unwrap();
|
||||
@@ -192,7 +192,7 @@ fn remove_cr(bencher: Bencher) {
|
||||
let temp_dir = tempfile::tempdir().unwrap();
|
||||
let mut data = Vec::new();
|
||||
for i in 0..500_000 {
|
||||
let line = format!("line {} with windows endings\r\n", i);
|
||||
let line = format!("line {i} with windows endings\r\n");
|
||||
data.extend_from_slice(line.as_bytes());
|
||||
}
|
||||
let file_path = create_test_file(&data, temp_dir.path());
|
||||
@@ -240,7 +240,7 @@ fn number_fix(bencher: Bencher) {
|
||||
let cents = i % 100;
|
||||
let thousands = euros / 1000;
|
||||
let remainder = euros % 1000;
|
||||
let line = format!("{}.{:03},{:02}\n", thousands, remainder, cents);
|
||||
let line = format!("{thousands}.{remainder:03},{cents:02}\n");
|
||||
data.extend_from_slice(line.as_bytes());
|
||||
}
|
||||
let file_path = create_test_file(&data, temp_dir.path());
|
||||
|
||||
Generated
+489
-483
File diff suppressed because it is too large.
Load diff
+4
-4
@@ -12,10 +12,10 @@ console = "0.16.0"
|
||||
libfuzzer-sys = "0.4.7"
|
||||
libc = "0.2.153"
|
||||
tempfile = "3.15.0"
|
||||
rand = { version = "0.9.0", features = ["small_rng"] }
|
||||
similar = "2.5.0"
|
||||
uucore = { version = "0.5.0", features = ["libc"] }
|
||||
uufuzz = "0.5.0"
|
||||
rand = { version = "0.10.0" }
|
||||
similar = "3.0.0"
|
||||
uucore = { version = "0.9.0", features = ["libc"] }
|
||||
uufuzz = "0.9.0"
|
||||
|
||||
sed = { path = ".." }
|
||||
|
||||
|
||||
+23
-22
@@ -64,6 +64,8 @@ pub struct ProcessingContext {
|
||||
pub parsed_block_nesting: usize,
|
||||
/// Command associated with each label
|
||||
pub label_to_command_map: HashMap<String, Rc<RefCell<Command>>>,
|
||||
/// Commands with a (latchable and resetable) address range
|
||||
pub range_commands: Vec<Rc<RefCell<Command>>>,
|
||||
/// True if a substitution was made as specified in the t command
|
||||
pub substitution_made: bool,
|
||||
/// Elements to append at the end of each command processing cycle
|
||||
@@ -84,26 +86,15 @@ pub struct StringSpace {
|
||||
pub has_newline: bool, // True if \n-terminated
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug)]
|
||||
/// Types of address specifications that precede commands
|
||||
pub enum AddressType {
|
||||
Re, // Line that matches regex
|
||||
Line, // Specific line
|
||||
RelLine, // Relative line
|
||||
Last, // Last line
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Format of an address
|
||||
pub struct Address {
|
||||
pub atype: AddressType, // Address type
|
||||
pub value: AddressValue, // Line number or regex
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AddressValue {
|
||||
LineNumber(usize),
|
||||
Regex(Option<Regex>),
|
||||
pub enum Address {
|
||||
Re(Option<Regex>), // Line that matches (optional) regex
|
||||
Line(usize), // Specific line
|
||||
RelLine(usize), // Relative line
|
||||
Last, // Last line
|
||||
StepMatch(usize), // Lines matching specified step from first
|
||||
StepEnd(usize), // Range ending at specified step from first
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -114,6 +105,16 @@ pub enum ReplacementPart {
|
||||
Group(u32), // \1 to \9
|
||||
}
|
||||
|
||||
// The maximum value allowed in regex quantifier
|
||||
pub const RE_DUP_MAX: usize = 32767;
|
||||
|
||||
/// Regex modes (BRE or ERE)
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum RegexMode {
|
||||
Basic,
|
||||
Extended,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// All specified replacements for an RE
|
||||
pub struct ReplacementTemplate {
|
||||
@@ -171,12 +172,12 @@ impl ReplacementTemplate {
|
||||
ReplacementPart::Literal(s) => result.push_str(s),
|
||||
|
||||
ReplacementPart::WholeMatch => {
|
||||
result.push_str(caps.get(0)?.map(|m| m.as_str()).unwrap_or(""));
|
||||
result.push_str(caps.get(0)?.map(|m| m.as_str()).unwrap_or_default());
|
||||
}
|
||||
|
||||
ReplacementPart::Group(n) => {
|
||||
let i: usize = (*n).try_into().unwrap();
|
||||
result.push_str(caps.get(i)?.map(|m| m.as_str()).unwrap_or(""));
|
||||
result.push_str(caps.get(i)?.map(|m| m.as_str()).unwrap_or_default());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -278,7 +279,7 @@ pub struct Command {
|
||||
pub addr1: Option<Address>, // Start address
|
||||
pub addr2: Option<Address>, // End address
|
||||
pub non_select: bool, // True if '!'
|
||||
pub start_line: Option<usize>, // Start line number (or None)
|
||||
pub start_line: Option<usize>, // Start line number (or None if unlatched)
|
||||
pub data: CommandData, // Command-specific data
|
||||
pub next: Option<Rc<RefCell<Command>>>, // Pointer to next command
|
||||
pub location: ScriptLocation, // Command's definition location
|
||||
|
||||
Loaded 20 of 53 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user