diff --git a/Dockerfile b/Dockerfile index 86788347..1d883c66 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,10 +52,13 @@ FROM python-base AS final COPY --from=dependencies $PYSETUP_PATH $PYSETUP_PATH # Add wrappers for entrypoints that provide support for the actions +COPY ./actions/common.sh / COPY ./actions/autosync/auto-sync-entrypoint.sh / COPY ./actions/rules-transform/rules-transform-entrypoint.sh / COPY ./actions/create-cd/create-cd-entrypoint.sh / -RUN chmod +x /auto-sync-entrypoint.sh /rules-transform-entrypoint.sh /create-cd-entrypoint.sh +COPY ./actions/sync-upstreams/sync-upstreams-entrypoint.sh / + +RUN chmod +x /auto-sync-entrypoint.sh /rules-transform-entrypoint.sh /create-cd-entrypoint.sh /sync-upstreams-entrypoint.sh ENTRYPOINT ["python3.9", "-m" , "trestlebot"] CMD ["--help"] diff --git a/actions/README.md b/actions/README.md index 3c8c2e6d..3a450dd9 100644 --- a/actions/README.md +++ b/actions/README.md @@ -2,7 +2,7 @@ ## Introduction -This document provides instructions and examples for creating and using GitHub Actions in the "trestle-bot" project. GitHub Actions are used to automate various tasks related to workspace management and checks. +This document provides instructions and examples for creating and using GitHub Actions in the `trestle-bot` project. GitHub Actions are used to automate various tasks related to workspace management and checks. ## Directory Structure @@ -21,9 +21,10 @@ For more details, consult the [GitHub Actions documentation](https://docs.github ## Examples -Here are examples of workflow snippets that demonstrate how to use these actions in the "trestle-bot" project. Each example includes a clear explanation of its purpose and the steps involved. +Here are examples of workflow snippets that demonstrate how to use these actions in the `trestle-bot` project. +See each action README for more details about the inputs and outputs. -### Create a New Component +### Create a New Component Definition ```yaml name: create @@ -104,7 +105,40 @@ jobs: branch: ${{ github.head_ref }} ``` -## Component Regeneration +## Propagate changes from upstream sources + +### Storing and syncing upstream content + +> Note: The upstream repo must be a valid trestle workspace. + +```yaml +name: Sync Upstream + +on: + schedule: + - cron: '0 0 * * *' + +jobs: + upstream-sync: + name: Sync upstream content + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + - name: Run trestlebot + id: trestlebot + uses: RedHatProductSecurity/trestle-bot/actions/sync-upstreams@main + with: + branch: "sync-upstream-${{ github.run_id }}" + target_branch: "main" + github_token: ${{ secrets.GITHUB_TOKEN }} + sources: | + https://github.com/myorg/myprofiles@main +``` + +### Component Definition Regeneration This example demonstrates how to use outputs and also includes labeling pull requests. @@ -121,8 +155,8 @@ on: - 'catalogs/**' jobs: - regeneration-content: - name: Regeneration the component definition + regenerate-content: + name: Regenerate the component definition runs-on: ubuntu-latest permissions: contents: write @@ -144,4 +178,4 @@ jobs: with: pr-number: | ${{ steps.trestlebot.outputs.pr_number }} -``` +``` \ No newline at end of file diff --git a/actions/autosync/auto-sync-entrypoint.sh b/actions/autosync/auto-sync-entrypoint.sh index 21592ea6..ba03a2d3 100644 --- a/actions/autosync/auto-sync-entrypoint.sh +++ b/actions/autosync/auto-sync-entrypoint.sh @@ -2,22 +2,10 @@ set -eu -# Manage newest git versions (related to CVE https://github.blog/2022-04-12-git-security-vulnerability-announced/) -# -if [ -z ${GITHUB_WORKSPACE+x} ]; then - echo "Setting git safe.directory default: /github/workspace ..." - git config --global --add safe.directory /github/workspace -else - echo "Setting git safe.directory GITHUB_WORKSPACE: $GITHUB_WORKSPACE ..." - git config --global --add safe.directory "$GITHUB_WORKSPACE" -fi +# shellcheck disable=SC1091 +source /common.sh -if [ -z ${INPUT_REPOSITORY+x} ]; then - echo "Skipping setting working directory as safe directory" -else - echo "Setting git safe.directory default: $INPUT_REPOSITORY ..." - git config --global --add safe.directory "$INPUT_REPOSITORY" -fi +set_git_safe_directory # Initialize the command variable command="trestlebot-autosync \ @@ -63,20 +51,4 @@ if [[ -n ${INPUT_TARGET_BRANCH} ]]; then command+=" --with-token - <<<\"${GITHUB_TOKEN}\"" fi -exec 3>&1 -output=$(eval "$command" > >(tee /dev/fd/3) 2>&1) - -commit=$(echo "$output" | grep "Commit Hash:" | sed 's/.*: //') - -if [ -n "$commit" ]; then - echo "changes=true" >> "$GITHUB_OUTPUT" - echo "commit=$commit" >> "$GITHUB_OUTPUT" -else - echo "changes=false" >> "$GITHUB_OUTPUT" -fi - -pr_number=$(echo "$output" | grep "Pull Request Number:" | sed 's/.*: //') - -if [ -n "$pr_number" ]; then - echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT" -fi \ No newline at end of file +execute_command "${command}" \ No newline at end of file diff --git a/actions/common.sh b/actions/common.sh new file mode 100644 index 00000000..8480169a --- /dev/null +++ b/actions/common.sh @@ -0,0 +1,45 @@ +# shellcheck disable=SC2148 + +# common.sh +# This file is sourced by other scripts and contains common functions + +# Manage newest git versions (related to CVE https://github.blog/2022-04-12-git-security-vulnerability-announced/) +# +function set_git_safe_directory() { + if [[ -z "${GITHUB_WORKSPACE+x}" ]]; then + echo "Setting git safe.directory default: /github/workspace ..." + git config --global --add safe.directory /github/workspace + else + echo "Setting git safe.directory GITHUB_WORKSPACE: $GITHUB_WORKSPACE ..." + git config --global --add safe.directory "$GITHUB_WORKSPACE" + fi + + if [[ -z "${INPUT_REPOSITORY+x}" ]]; then + echo "Skipping setting working directory as safe directory" + else + echo "Setting git safe.directory default: $INPUT_REPOSITORY ..." + git config --global --add safe.directory "$INPUT_REPOSITORY" + fi +} + +# Execute the command and set the output variables for GitHub Actions +function execute_command() { + local command=$1 + exec 3>&1 + output=$(eval "$command" > >(tee /dev/fd/3) 2>&1) + + commit=$(echo "$output" | grep "Commit Hash:" | sed 's/.*: //') + + if [ -n "$commit" ]; then + echo "changes=true" >> "$GITHUB_OUTPUT" + echo "commit=$commit" >> "$GITHUB_OUTPUT" + else + echo "changes=false" >> "$GITHUB_OUTPUT" + fi + + pr_number=$(echo "$output" | grep "Pull Request Number:" | sed 's/.*: //') + + if [ -n "$pr_number" ]; then + echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT" + fi +} diff --git a/actions/create-cd/create-cd-entrypoint.sh b/actions/create-cd/create-cd-entrypoint.sh index f28939f2..5fd93b27 100644 --- a/actions/create-cd/create-cd-entrypoint.sh +++ b/actions/create-cd/create-cd-entrypoint.sh @@ -2,22 +2,10 @@ set -eu -# Manage newest git versions (related to CVE https://github.blog/2022-04-12-git-security-vulnerability-announced/) -# -if [ -z ${GITHUB_WORKSPACE+x} ]; then - echo "Setting git safe.directory default: /github/workspace ..." - git config --global --add safe.directory /github/workspace -else - echo "Setting git safe.directory GITHUB_WORKSPACE: $GITHUB_WORKSPACE ..." - git config --global --add safe.directory "$GITHUB_WORKSPACE" -fi +# shellcheck disable=SC1091 +source /common.sh -if [ -z ${INPUT_REPOSITORY+x} ]; then - echo "Skipping setting working directory as safe directory" -else - echo "Setting git safe.directory default: $INPUT_REPOSITORY ..." - git config --global --add safe.directory "$INPUT_REPOSITORY" -fi +set_git_safe_directory # Initialize the command variable command="trestlebot-create-cd \ @@ -54,20 +42,4 @@ if [[ -n ${INPUT_TARGET_BRANCH} ]]; then command+=" --with-token - <<<\"${GITHUB_TOKEN}\"" fi -exec 3>&1 -output=$(eval "$command" > >(tee /dev/fd/3) 2>&1) - -commit=$(echo "$output" | grep "Commit Hash:" | sed 's/.*: //') - -if [ -n "$commit" ]; then - echo "changes=true" >> "$GITHUB_OUTPUT" - echo "commit=$commit" >> "$GITHUB_OUTPUT" -else - echo "changes=false" >> "$GITHUB_OUTPUT" -fi - -pr_number=$(echo "$output" | grep "Pull Request Number:" | sed 's/.*: //') - -if [ -n "$pr_number" ]; then - echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT" -fi \ No newline at end of file +execute_command "${command}" \ No newline at end of file diff --git a/actions/rules-transform/rules-transform-entrypoint.sh b/actions/rules-transform/rules-transform-entrypoint.sh index 970ffbb0..8fb9d814 100644 --- a/actions/rules-transform/rules-transform-entrypoint.sh +++ b/actions/rules-transform/rules-transform-entrypoint.sh @@ -2,22 +2,10 @@ set -eu -# Manage newest git versions (related to CVE https://github.blog/2022-04-12-git-security-vulnerability-announced/) -# -if [ -z ${GITHUB_WORKSPACE+x} ]; then - echo "Setting git safe.directory default: /github/workspace ..." - git config --global --add safe.directory /github/workspace -else - echo "Setting git safe.directory GITHUB_WORKSPACE: $GITHUB_WORKSPACE ..." - git config --global --add safe.directory "$GITHUB_WORKSPACE" -fi +# shellcheck disable=SC1091 +source /common.sh -if [ -z ${INPUT_REPOSITORY+x} ]; then - echo "Skipping setting working directory as safe directory" -else - echo "Setting git safe.directory default: $INPUT_REPOSITORY ..." - git config --global --add safe.directory "$INPUT_REPOSITORY" -fi +set_git_safe_directory # Initialize the command variable command="trestlebot-rules-transform \ @@ -49,20 +37,4 @@ if [[ -n ${INPUT_TARGET_BRANCH} ]]; then command+=" --with-token - <<<\"${GITHUB_TOKEN}\"" fi -exec 3>&1 -output=$(eval "$command" > >(tee /dev/fd/3) 2>&1) - -commit=$(echo "$output" | grep "Commit Hash:" | sed 's/.*: //') - -if [ -n "$commit" ]; then - echo "changes=true" >> "$GITHUB_OUTPUT" - echo "commit=$commit" >> "$GITHUB_OUTPUT" -else - echo "changes=false" >> "$GITHUB_OUTPUT" -fi - -pr_number=$(echo "$output" | grep "Pull Request Number:" | sed 's/.*: //') - -if [ -n "$pr_number" ]; then - echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT" -fi \ No newline at end of file +execute_command "${command}" \ No newline at end of file diff --git a/actions/sync-upstreams/README.md b/actions/sync-upstreams/README.md new file mode 100644 index 00000000..d533f75e --- /dev/null +++ b/actions/sync-upstreams/README.md @@ -0,0 +1,120 @@ +# Trestle Bot Sync Upstreams Action + +## Basic Configuration + + +```yaml + +name: Example Workflow +... + + steps: + - uses: actions/checkout@v3 + - name: Run trestlebot + id: trestlebot + uses: RedHatProductSecurity/trestle-bot/actions/sync-upstreams@main + with: + sources: https://github.com/myorg/myprofiles@main +``` + +## Action Inputs + + +| Name | Description | Default | Required | +| --- | --- | --- | --- | +| sources | A newline separated list of upstream sources to sync with a repo@branch format. For example, `https://github.com/myorg/myprofiles@main` | None | True | +| github_token | GitHub token used to make authenticated API requests | None | False | +| include_model_names | Comma-separated list of model names by name to include in the sync. For example, `component-definition,system-security-plan`. Defaults to all models. | None | False | +| exclude_model_names | Comma-separated list of model names to exclude from the sync. For example, `component-definition,system-security-plan` | None | False | +| skip_validation | Skip validation of the upstream OSCAL content. Defaults to false | false | False | +| commit_message | Commit message | Sync automatic updates | False | +| pull_request_title | Custom pull request title | Automatic updates from trestlebot | False | +| branch | Name of the Git branch to which modifications should be pushed. Required if Action is used on the `pull_request` event. | ${{ github.ref_name }} | False | +| target_branch | Target branch (or base branch) to create a pull request against. If unset, no pull request will be created. If set, a pull request will be created using the `branch` field as the head branch. | None | False | +| file_pattern | Comma-separated file pattern list used for `git add`. For example `component-definitions/*,*json`. Defaults to (`.`) | . | False | +| repository | Local file path to the git repository. Defaults to the current directory (`.`) | . | False | +| commit_user_name | Name used for the commit user | github-actions[bot] | False | +| commit_user_email | Email address used for the commit user | 41898282+github-actions[bot]@users.noreply.github.com | False | +| commit_author_name | Name used for the commit author. Defaults to the username of whoever triggered this workflow run. | ${{ github.actor }} | False | +| commit_author_email | Email address used for the commit author. Defaults to the email of whoever triggered this workflow run. | ${{ github.actor }}@users.noreply.github.com | False | +| verbose | Enable verbose logging | false | False | + + + +## Action Outputs + + +| Name | Description | +| --- | --- | +| changes | Value is "true" if changes were committed back to the repository. | +| commit | Full hash of the created commit. Only present if the "changes" output is "true". | +| pr_number | Number of the submitted pull request. Only present if a pull request is submitted. | + + + +## Action Behavior + +The purpose of this action is to sync a local repository with upstream sources. The action will clone the repository, sync the upstream sources, validate the OSCAL content, and commit the changes back to the repository. The action can also be configured to create a pull request with the changes. + +> IMPORTANT: Both the upstream repo and the local repo must be valid trestle workspaces. + +Below are the main use-cases/workflows available: + +- The default behavior of this action is to run sync the changes to the workspace and commit the changes back to the branch the workflow ran from ( `github.ref_name` ). The branch can be changed by setting the field `branch`. If no changes exist or the changes do not exist with the file pattern set, no changes will be made and the action will exit successfully. + +```yaml + steps: + - uses: actions/checkout@v3 + - name: Run trestlebot + id: trestlebot + uses: RedHatProductSecurity/trestle-bot/actions/sync-upstreams@main + with: + sources: https://github.com/myorg/myprofiles@main + branch: "another-branch" +``` + +- If the `target_branch` field is set, a pull request will be made using the `target_branch` as the base branch and `branch` as the head branch. + +```yaml + steps: + - uses: actions/checkout@v3 + - name: Run trestlebot + id: trestlebot + uses: RedHatProductSecurity/trestle-bot/actions/sync-upstreams@main + with: + sources: https://github.com/myorg/myprofiles@main + branch: "autoupdate-${{ github.run_id }}" + target_branch: "main" + github_token: ${{ secret.GITHUB_TOKEN }} +``` + +- Update more than one upstream source: + +```yaml + steps: + - uses: actions/checkout@v3 + - name: Run trestlebot + id: trestlebot + uses: RedHatProductSecurity/trestle-bot/actions/sync-upstreams@main + with: + sources: | + https://github.com/myorg/myprofiles@main + https://github.com/myorg/mycomps@main +`````` + +- Use includes and excludes to limit the models that are synced: + + This example only syncs model content that contain the string "rev5" in the name. + +```yaml + steps: + - uses: actions/checkout@v3 + - name: Run trestlebot + id: trestlebot + uses: RedHatProductSecurity/trestle-bot/actions/sync-upstreams@main + with: + sources: | + https://github.com/myorg/myprofiles@main + https://github.com/myorg/mycomps@main + include_model_names: "*rev5*" +``` \ No newline at end of file diff --git a/actions/sync-upstreams/action.yml b/actions/sync-upstreams/action.yml new file mode 100644 index 00000000..01b26e7b --- /dev/null +++ b/actions/sync-upstreams/action.yml @@ -0,0 +1,83 @@ +name: "trestle-bot-sync-upstreams" +author: "Red Hat Product Security" +description: "An action to sync and validate OSCAL content from upstream repositories." + +inputs: + sources: + description: "A newline separated list of upstream sources to sync with a repo@branch format. For example, `https://github.com/myorg/myprofiles@main`" + required: true + github_token: + description: "GitHub token used to make authenticated API requests" + required: false + include_model_names: + description: "Comma-separated list of model names by name to include in the sync. For example, `component-definition,system-security-plan`. Defaults to all models." + required: false + exclude_model_names: + description: "Comma-separated list of model names to exclude from the sync. For example, `component-definition,system-security-plan`" + required: false + skip_validation: + description: "Skip validation of the upstream OSCAL content. Defaults to false" + required: false + default: "false" + commit_message: + description: Commit message + required: false + default: "Sync automatic updates" + pull_request_title: + description: Custom pull request title + required: false + default: "Automatic updates from trestlebot" + branch: + description: Name of the Git branch to which modifications should be pushed. Required if Action is used on the `pull_request` event. + required: false + default: ${{ github.ref_name }} + target_branch: + description: Target branch (or base branch) to create a pull request against. If unset, no pull request will be created. If set, a pull request will be created using the `branch` field as the head branch. + required: false + file_pattern: + description: Comma-separated file pattern list used for `git add`. For example `component-definitions/*,*json`. Defaults to (`.`) + required: false + default: '.' + repository: + description: Local file path to the git repository. Defaults to the current directory (`.`) + required: false + default: '.' + commit_user_name: + description: Name used for the commit user + required: false + default: github-actions[bot] + commit_user_email: + description: Email address used for the commit user + required: false + default: 41898282+github-actions[bot]@users.noreply.github.com + commit_author_name: + description: Name used for the commit author. Defaults to the username of whoever triggered this workflow run. + required: false + default: ${{ github.actor }} + commit_author_email: + description: Email address used for the commit author. Defaults to the email of whoever triggered this workflow run. + required: false + default: ${{ github.actor }}@users.noreply.github.com + verbose: + description: Enable verbose logging + required: false + default: "false" + +outputs: + changes: + description: Value is "true" if changes were committed back to the repository. + commit: + description: Full hash of the created commit. Only present if the "changes" output is "true". + pr_number: + description: Number of the submitted pull request. Only present if a pull request is submitted. + +runs: + using: "docker" + image: "../../Dockerfile" + entrypoint: "/sync-upstreams-entrypoint.sh" + env: + GITHUB_TOKEN: ${{ inputs.github_token }} + +branding: + icon: "check" + color: "green" diff --git a/actions/sync-upstreams/sync-upstreams-entrypoint.sh b/actions/sync-upstreams/sync-upstreams-entrypoint.sh new file mode 100644 index 00000000..60a07d42 --- /dev/null +++ b/actions/sync-upstreams/sync-upstreams-entrypoint.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +set -eu + +# shellcheck disable=SC1091 +source /common.sh + +set_git_safe_directory + +# Transform the input sources into a comma separated list +INPUT_SOURCES=$(echo "${INPUT_SOURCES}" | tr '\n' ' ' | tr -s ' ' | sed 's/ *$//' | tr ' ' ',') + +# Initialize the command variable +command="trestlebot-sync-upstreams \ + --sources=\"${INPUT_SOURCES}\" \ + --include-model-names=\"${INPUT_INCLUDE_MODEL_NAMES}\" \ + --exclude-model-names=\"${INPUT_EXCLUDE_MODEL_NAMES}\" \ + --commit-message=\"${INPUT_COMMIT_MESSAGE}\" \ + --pull-request-title=\"${INPUT_PULL_REQUEST_TITLE}\" \ + --branch=\"${INPUT_BRANCH}\" \ + --file-patterns=\"${INPUT_FILE_PATTERN}\" \ + --committer-name=\"${INPUT_COMMIT_USER_NAME}\" \ + --committer-email=\"${INPUT_COMMIT_USER_EMAIL}\" \ + --author-name=\"${INPUT_COMMIT_AUTHOR_NAME}\" \ + --author-email=\"${INPUT_COMMIT_AUTHOR_EMAIL}\" \ + --working-dir=\"${INPUT_REPOSITORY}\" \ + --target-branch=\"${INPUT_TARGET_BRANCH}\"" + +# Conditionally include flags +if [[ ${INPUT_VERBOSE} == true ]]; then + command+=" --verbose" +fi + +if [[ ${INPUT_SKIP_VALIDATION} == true ]]; then + command+=" --skip-validation" +fi + +# Only set the token value when is a target branch so pull requests can be created +if [[ -n ${INPUT_TARGET_BRANCH} ]]; then + if [[ -z ${GITHUB_TOKEN} ]]; then + echo "Set the GITHUB_TOKEN env variable." + exit 1 + fi + + command+=" --with-token - <<<\"${GITHUB_TOKEN}\"" +fi + +execute_command "${command}" \ No newline at end of file