Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/workflows/tests-read.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: tests-read

on:
pull_request:
workflow_call:

# read only needs to read; the seed step uses write, which needs write.
permissions:
Expand All @@ -21,6 +21,11 @@ jobs:
marker: test-read
data: '{"version":"4.5.6","reviewed":true}'

# The seed's pulls.update is not immediately consistent: a pulls.get
# right after it can hit a replica that hasn't caught up. Give it a
# moment to propagate before reading back.
- run: sleep 10

# Read via an explicit pr-number so the action takes the REST fetch path.
# The default path reads the event payload's PR body, which is captured
# when the run starts and so can't see the seed written above this run.
Expand Down
40 changes: 39 additions & 1 deletion .github/workflows/tests-write.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: tests-write

on:
pull_request:
workflow_call:

# write updates the PR body via the REST API
permissions:
Expand All @@ -21,6 +21,11 @@ jobs:
marker: test-write
data: '{"version":"1.2.3","reviewed":false}'

# pulls.update is not immediately consistent, and the merge step below
# reads the body back to find the seeded marker. Let the seed propagate
# first, otherwise merge can miss it and append a duplicate block.
- run: sleep 10

# Merge: shallow-merge new keys into the existing blob (merge=true).
- id: merge
uses: ./write
Expand All @@ -29,6 +34,9 @@ jobs:
data: '{"reviewed":true,"author":"test"}'
merge: "true"

# Same propagation delay before reading the merged result back.
- run: sleep 10

# Read it back to assert the round-trip survived the composite-input plumbing.
# Explicit pr-number forces the REST fetch path; the default path reads the
# event payload body captured at run start, before the writes above.
Expand All @@ -54,3 +62,33 @@ jobs:
[ "$(echo "$RESULT" | jq -r '.author')" = "test" ] || { echo "::error::author not added"; exit 1; }

echo "write self-test passed"

# The write action exposes what it persisted as `result`/`changed` outputs.
- name: Assert write outputs
env:
SEED_RESULT: ${{ steps.seed.outputs.result }}
SEED_CHANGED: ${{ steps.seed.outputs.changed }}
MERGE_RESULT: ${{ steps.merge.outputs.result }}
MERGE_CHANGED: ${{ steps.merge.outputs.changed }}
RESULT: ${{ steps.read.outputs.result }}
run: |
echo "seed.result=$SEED_RESULT seed.changed=$SEED_CHANGED"
echo "merge.result=$MERGE_RESULT merge.changed=$MERGE_CHANGED"

# Both writes mutate the body, so changed must be true.
[ "$SEED_CHANGED" = "true" ] || { echo "::error::seed.changed not true"; exit 1; }
[ "$MERGE_CHANGED" = "true" ] || { echo "::error::merge.changed not true"; exit 1; }

# Replace output reflects exactly what was passed in.
[ "$(echo "$SEED_RESULT" | jq -r '.version')" = "1.2.3" ] || { echo "::error::seed.result version wrong"; exit 1; }
[ "$(echo "$SEED_RESULT" | jq -r '.reviewed')" = "false" ] || { echo "::error::seed.result reviewed wrong"; exit 1; }

# Merge output reflects the post-merge value (old ∪ new).
[ "$(echo "$MERGE_RESULT" | jq -r '.version')" = "1.2.3" ] || { echo "::error::merge.result version not preserved"; exit 1; }
[ "$(echo "$MERGE_RESULT" | jq -r '.reviewed')" = "true" ] || { echo "::error::merge.result reviewed not merged"; exit 1; }
[ "$(echo "$MERGE_RESULT" | jq -r '.author')" = "test" ] || { echo "::error::merge.result author not added"; exit 1; }

# The merge output must match what a subsequent read sees.
[ "$(echo "$MERGE_RESULT" | jq -S .)" = "$(echo "$RESULT" | jq -S .)" ] || { echo "::error::merge.result does not match read result"; exit 1; }

echo "write outputs self-test passed"
20 changes: 20 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: tests

on:
pull_request:

# Both reusable workflows seed the PR body via the REST API.
permissions:
pull-requests: write
contents: read

# Run the two suites as a chain rather than in parallel: each one does a
# whole-body read-modify-write on the same PR description, so running them
# concurrently makes their writes clobber each other. `needs` serializes them.
jobs:
read:
uses: ./.github/workflows/tests-read.yaml

write:
needs: read
uses: ./.github/workflows/tests-write.yaml
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ permissions:

When `merge` is `true`, both the existing and incoming values must be plain JSON objects.

### Outputs

| Name | Description |
| --------- | --------------------------------------------------------------------------------- |
| `result` | JSON-stringified metadata that was written, after the merge if `merge` is enabled |
| `changed` | `"true"` if the PR body was updated, `"false"` if it was already up to date |

`result` is the effective stored value, which is the most reliable way to learn the result of a `merge: "true"` write without reading the body back.

### Example

```yaml
Expand All @@ -81,10 +90,12 @@ jobs:
write-metadata:
runs-on: ubuntu-latest
steps:
- uses: taskworld/pr-meta-action/write@v1
- id: meta
uses: taskworld/pr-meta-action/write@v1
with:
data: '{"reviewed":true,"version":"1.2.3"}'
merge: "true"
- run: echo 'wrote ${{ steps.meta.outputs.result }} (changed=${{ steps.meta.outputs.changed }})'
```

## Notes
Expand Down
15 changes: 14 additions & 1 deletion write/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ inputs:
default: "false"
required: false

outputs:
result:
description: JSON-stringified metadata that was written (after merge, if enabled)
value: ${{ steps.write.outputs.result }}
changed:
description: true when the PR body was updated, false when it was already up to date
value: ${{ steps.write.outputs.changed }}

runs:
using: composite
steps:
Expand Down Expand Up @@ -95,11 +103,16 @@ runs:
block :
`${body}\n\n${block}\n`

if (newBody !== body) {
const changed = newBody !== body

if (changed) {
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
body: newBody,
})
}

core.setOutput('result', JSON.stringify(newVal))
core.setOutput('changed', String(changed))