mirror of
https://code.forgejo.org/actions/cascading-pr
synced 2025-03-15 06:46:59 +01:00

When running from a PR, the ref is the head of the PR in the origin repository. The PR is used to: * display comments about the location of the destination PR * asynchronously close / merge the destination PR when something happens in the origin PR When only the ref is available, the destination PR must be closed and the corresponding branch destroyed immediately after it concludes because there is no convenient way to know what or when something else will happen.
245 lines
10 KiB
Markdown
245 lines
10 KiB
Markdown
Create and synchronize a PR in a dependent repository
|
|
|
|
<!-- action-docs-description -->
|
|
## Description
|
|
|
|
|
|
If repository A depends on repository B, `cascadinging-pr` can be used
|
|
by a workflow in repository B to trigger the CI on repository A and
|
|
verify it passes when using a modified version of repository B. This
|
|
modified version could be a pull request, a branch or a reference.
|
|
|
|
In the simplest case `cascading-pr` runs a workflow in `destination-repo`
|
|
that uses `origin-ref` and blocks until it completes.
|
|
|
|
As an example, when a tag is set in Forgejo and builds a new release,
|
|
it is concluded by a call to `cascading-pr` that runs
|
|
[end-to-end](https://code.forgejo.org/forgejo/end-to-end/) tests on
|
|
the newly built release to verify it works as expected.
|
|
|
|
When used in a workflow triggered by a PR event in `origin-repo`,
|
|
`cascading-pr` will create, update and close a matching PR in the
|
|
`destination-repo`. When the PR is updated, `cascading-pr` will
|
|
update the matching PR. It waits for the workflow triggered by these
|
|
updates in `destination-repo` to complete. If fails, `cascading-pr`,
|
|
also fails.
|
|
|
|
As an example, when a PR is created in
|
|
[`forgejo/runner`](https://code.forgejo.org/forgejo/runner/), a
|
|
matching PR is created in
|
|
[`actions/setup-forgejo`](https://code.forgejo.org/actions/setup-forgejo/)
|
|
with the proposed change and `cascading-pr` waits until the CI in
|
|
`actions/setup-forgejo` is successful.
|
|
|
|
The `update` script is expected to be found in `origin-repo` and is
|
|
given the following arguments:
|
|
|
|
* A directory path in which the `destination-branch` of `destination-repo`
|
|
(or a fork) is checked-out.
|
|
* The path to a JSON file describing the pull request in `destination-repo`.
|
|
* A directory path in which the head of `origin-repo` is checked-out at:
|
|
* if `origin-pr` is specified, the head branch of `origin-pr`
|
|
* otherwise `origin-ref`
|
|
* Information about the `origin-repo`
|
|
* if `origin-pr` is specified, the path to a JSON file desccribing
|
|
the pull request in `origin-repo`
|
|
* otherwise `origin-ref`
|
|
|
|
If changes are found in the destination repository directory after
|
|
the `update` script runs, they will be pushed as a new commit in the
|
|
PR in the `destination-repo`.
|
|
|
|
`origin-token` is used when accessing `origin-repo` and needs the
|
|
`read:user`, `read:repository` and `write:issue` scopes.
|
|
|
|
`destination-token` is used to push the branch that contains an
|
|
update to `destination-repo` (or `destination-fork-repo`) and open a
|
|
pull request. It needs the `read:user`, `write:repository` and
|
|
`write:issue` scopes.
|
|
|
|
It is recommended that a dedicated user is used to create
|
|
`destination-token` and that `destination-fork-repo` is always used
|
|
unless the users who are able to create pull requests are trusted.
|
|
|
|
When the PR in the `destination-repo` is from a forked repository,
|
|
the `update` script is run from the default branch of
|
|
`destination-repo` instead of the head of the PR which is a branch
|
|
in destination-fork-repo. The PR author must not be trusted and it
|
|
is imperative that the `update` script never runs anything found in
|
|
the head branch of the PR.
|
|
|
|
If the fork of the destination repository is specified and it does
|
|
not exist, it is created.
|
|
<!-- action-docs-description -->
|
|
<!-- action-docs-inputs -->
|
|
## Inputs
|
|
|
|
| parameter | description | required | default |
|
|
| --- | --- | --- | --- |
|
|
| origin-url | URL of the Forgejo instance where the PR that triggers the action is located (e.g. https://code.forgejo.org) | `true` | |
|
|
| origin-repo | the repository in which the PR was created | `true` | |
|
|
| origin-token | a token with write permission on origin-repo | `true` | |
|
|
| origin-pr | number of the PR in {orign-repo}, mutually exclusive with {origin-ref} | `false` | |
|
|
| origin-ref | reference in {orign-repo}, mutually exclusive with {origin-pr} | `false` | |
|
|
| destination-url | URL of the Forgejo instance where the cascading PR is created or updated (e.g. https://code.forgejo.org) | `true` | |
|
|
| destination-repo | the repository in which the cascading PR is created or updated | `true` | |
|
|
| destination-fork-repo | the fork of {destination-repo} in which the {destination-branch} will be created or updated | `false` | |
|
|
| destination-branch | the base branch of the destination repository for the cascading PR | `true` | |
|
|
| destination-token | a token with write permission on destination-repo | `true` | |
|
|
| update | path to the script to update the content of the cascading PR | `true` | |
|
|
| prefix | prefix of the cascading PR created on destination-repo (default to {origin-repo}) | `false` | |
|
|
| close-merge | if true the cascading PR will be closed and the branch deleted when the PR is merged | `false` | false |
|
|
| verbose | if true print verbose information | `false` | false |
|
|
| debug | if true print debug information | `false` | false |
|
|
<!-- action-docs-inputs -->
|
|
|
|
# Forgejo dependencies
|
|
|
|
The [Forgejo](https://codeberg.org/forgejo/forgejo/) repositories that depend on each other are
|
|
linked with workflows using `cascading-pr` as follows.
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
lxc-helper(lxc-helper) --> act(act)
|
|
act --> runner(Forgejo runner)
|
|
runner --> setup-forgejo(setup-forgejo)
|
|
setup-forgejo --> e2e(end-to-end)
|
|
forgejo-curl(forgejo-curl.sh) --> setup-forgejo
|
|
forgejo(forgejo) --> e2e
|
|
|
|
click lxc-helper "https://code.forgejo.org/forgejo/lxc-helpers/src/branch/main/.forgejo/workflows/cascade-act.yml"
|
|
click act "https://code.forgejo.org/forgejo/act/src/branch/main/.forgejo/workflows/cascade-runner.yml"
|
|
click runner "https://code.forgejo.org/forgejo/runner/src/branch/main/.forgejo/workflows/cascade-setup-forgejo.yml"
|
|
click setup-forgejo "https://code.forgejo.org/actions/setup-forgejo/src/branch/main/.forgejo/workflows/cascade-end-to-end.yml"
|
|
click e2e "https://code.forgejo.org/actions/end-to-end"
|
|
click forgejo-curl "https://code.forgejo.org/forgejo/forgejo-curl/src/branch/main/.forgejo/workflows/cascade-setup-forgejo.yml"
|
|
click forgejo "https://codeberg.org/forgejo/forgejo/src/branch/forgejo/.forgejo/workflows/cascade-setup-end-to-end.yml"
|
|
```
|
|
|
|
# Example workflow
|
|
|
|
```yaml
|
|
on:
|
|
pull_request:
|
|
types:
|
|
- opened
|
|
- synchronize
|
|
- closed
|
|
|
|
jobs:
|
|
test:
|
|
runs-on: docker
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- uses: actions/cascading-pr@v1
|
|
with:
|
|
origin-url: https://code.forgejo.org
|
|
origin-repo: forgejo/lxc-helpers
|
|
origin-token: ${{ secrets.ORIGIN_TOKEN }}
|
|
origin-pr: ${{ github.event.pull_request.number }}
|
|
destination-url: https://code.forgejo.org
|
|
destination-repo: forgejo/act
|
|
destination-branch: main
|
|
destination-token: ${{ secrets.DESTINATION_TOKEN }}
|
|
update: ./upgrade-lxc-helpers
|
|
```
|
|
|
|
# Pull requests from forked repositories
|
|
|
|
When `cascading-pr` runs as a consequence of pull request from a
|
|
forked repository, the workflow must be triggered by a `pull_request_target`
|
|
event otherwise it will not have access to secrets.
|
|
|
|
# Prevent privilege escalation
|
|
|
|
When `cascading-pr` runs as a consequence of a pull request from a
|
|
repository forked from `orgin-repo`, it should create a pull request
|
|
from a forked repository of `destination-repo` by specifying the
|
|
`destination-fork-repo`.
|
|
|
|
If the `destination-fork-repo` repository does not exist, it will be
|
|
created as a fork of the `destination-repo` repository, using
|
|
`destination-token`.
|
|
|
|
# Hacking
|
|
|
|
The test environment consists of the following (all users password is admin1234)
|
|
|
|
* A forgejo instance with a runner
|
|
* An unprivileged user user1
|
|
* The repository user1/originrepo
|
|
* contains a pull_request workflow using cascading-pr that targets user2/destinationrepo
|
|
* contains a script that will modify user2/destinationrepo
|
|
* a branch1 at the same commit as main
|
|
* The repository user1/cascading-pr with the action under test
|
|
* An unprivileged user user2
|
|
* The repository user2/destinationrepo
|
|
|
|
```sh
|
|
git clone https://code.forgejo.org/actions/setup-forgejo
|
|
export PATH=$(pwd)/setup-forgejo:$PATH
|
|
git clone https://code.forgejo.org/actions/cascading-pr
|
|
cd cascading-pr
|
|
export DIR=/tmp/forgejo-for-cascading-pr
|
|
forgejo-curl.sh logout
|
|
forgejo-runner.sh teardown
|
|
forgejo-binary.sh teardown
|
|
forgejo-binary.sh setup root admin1234 https://codeberg.org/forgejo/forgejo/releases/download/v1.21.3-0/forgejo-1.21.3-0-linux-amd64
|
|
FORGEJO_RUNNER_CONFIG=$(pwd)/tests/runner-config.yaml forgejo-runner.sh setup
|
|
url=$(cat $DIR/forgejo-url)
|
|
firefox $url
|
|
```
|
|
|
|
The test for a successful run of the cascading-pr action consists of:
|
|
|
|
* creating a PR from branch1 to main
|
|
* wait for the commit status until it is successful
|
|
|
|
## testing an update on the action
|
|
|
|
* run `tests/run.sh --debug` once so all is in place
|
|
* commit changes to the files that are in the cascading-pr action
|
|
(action.yml, cascading-pr.sh etc.)
|
|
* push the modified action to `user1/cascading-pr`
|
|
* visit $url/user1/originrepo/actions/runs/1 and click re-run
|
|
|
|
## interactive debugging
|
|
|
|
Following the steps below recreate the same environment as the
|
|
integration workflow locally. It is helpful for forensic analysis when
|
|
something does not run as expected and the error displayed are unclear.
|
|
|
|
To help with the development loop all steps are idempotent and
|
|
running `tests/run.sh --debug` multiple times must succeed.
|
|
|
|
Individual steps can be run independendely by using the name of the function.
|
|
For instance:
|
|
|
|
* `tests/run.sh --debug create_pull_request` will only call the `create_pull_request`
|
|
function found in `tests/run.sh` to (re)create the pull request in `user1/originrepo`.
|
|
* `./cascading-pr.sh --debug --origin-url ... upsert_branch` will only call the `upsert_branch`
|
|
function found in `cascading-pr.sh`.
|
|
|
|
## directories
|
|
|
|
The `tests/run.sh` script stores all its files in
|
|
`/tmp/cascading-pr-test`. The temporary directories created by
|
|
`cascading-pr.sh` are disposed of when the script ends.
|
|
|
|
## logging
|
|
|
|
If `--debug` is used a full debug log is displayed, very complete and
|
|
very verbose. Otherwise it is stashed in a temporary file and only
|
|
displayed if an error happens.
|
|
|
|
## snippets for copy/pasting
|
|
|
|
```sh
|
|
tests/run.sh --debug
|
|
tests/run.sh --debug no_change_no_cascade_pr
|
|
./cascading-pr.sh --debug --origin-url "$url" --origin-repo "user1/originrepo" --origin-token "$(cat /tmp/cascading-pr-test/user1/repo-token)" --origin-pr 1 --destination-url "$url" --destination-repo "user2/destinationrepo" --destination-token "$(cat /tmp/cascading-pr-test/user2/repo-token)" --destination-branch "main" --update "upgraded" run
|
|
```
|
|
|
|
## Update the README
|
|
|
|
With https://github.com/npalm/action-docs `action-docs --update-readme`
|