diff --git a/.github/workflows/gem_ci.yml b/.github/workflows/gem_ci.yml index dbb0d1f..933b8ca 100644 --- a/.github/workflows/gem_ci.yml +++ b/.github/workflows/gem_ci.yml @@ -73,7 +73,8 @@ jobs: if: | contains(inputs.rake_task, 'coverage') && inputs.runs_on == 'ubuntu-latest' && - inputs.ruby_version == '3.2' + inputs.ruby_version == '3.2' && + secrets.CODECOV_TOKEN uses: codecov/codecov-action@v4 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/labeller.yml b/.github/workflows/labeller.yml index ee149bf..9caa715 100644 --- a/.github/workflows/labeller.yml +++ b/.github/workflows/labeller.yml @@ -1,22 +1,22 @@ name: Labeller on: - issues: - types: - - opened - - labeled - - unlabeled - pull_request_target: - types: - - opened - - labeled - - unlabeled + workflow_call: + inputs: + token: + default: '' + type: string jobs: label: + name: ${{ github.event.action }} ${{ github.event_name }} + # case if the workflow is called improperly + if: | + contains(fromJson('["puppetlabs", "puppet-toy-chest"]'), github.repository_owner) && + contains(fromJson('["pull_request_target", "issues"]'), github.event_name) && + contains(fromJson('["opened", "reopened", "labeled", "unlabeled"]'), github.event.action) runs-on: ubuntu-latest steps: - - uses: puppetlabs/community-labeller@v1.0.1 name: Label issues or pull requests with: @@ -24,4 +24,4 @@ jobs: label_color: '5319e7' org_membership: puppetlabs fail_if_member: 'true' - token: ${{ secrets.IAC_COMMUNITY_LABELER }} + token: ${{ inputs.token != '' && inputs.token || secrets.IAC_COMMUNITY_TOKEN }} diff --git a/.github/workflows/mend_ruby.yml b/.github/workflows/mend_ruby.yml index 0e9bc84..aa02100 100644 --- a/.github/workflows/mend_ruby.yml +++ b/.github/workflows/mend_ruby.yml @@ -4,14 +4,42 @@ name: mend on: workflow_call: + inputs: + api_key: + default: '' + type: string + token: + default: '' + type: string + product_name: + default: 'content-and-tooling' + type: string -jobs: +env: + MEND_API_KEY: ${{ secrets.MEND_API_KEY != '' && secrets.MEND_API_KEY || inputs.api_key }} + MEND_TOKEN: ${{ secrets.MEND_TOKEN != '' && secrets.MEND_TOKEN || inputs.token }} + PRODUCT_NAME: ${{ inputs.PRODUCT_NAME != '' && inputs.PRODUCT_NAME || inputs.product_name }} + REQUIRE_SECRETS: MEND_API_KEY MEND_TOKEN +jobs: mend: runs-on: "ubuntu-latest" + continue-on-error: ${{ contains(fromJson('["puppetlabs","puppet-toy-chest"]'), github.repository_owner) != true }} steps: + - name: "check requirements" + run: | + declare -a MISSING + for V in ${REQUIRE_SECRETS} ; do + [[ -z "${!V}" ]] && MISSING+=($V) + done + if [ ${#MISSING[@]} -gt 0 ] ; then + echo "::warning::missing required secrets: ${MISSING[@]}" + exit 1 + fi + # If we are on a PR, checkout the PR head sha, else checkout the default branch - name: "Set the checkout ref" + if: success() id: set_ref run: | if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then @@ -21,32 +49,38 @@ jobs: fi - name: "checkout" + if: success() uses: "actions/checkout@v4" with: fetch-depth: 1 ref: ${{ steps.set_ref.outputs.ref }} - name: "setup ruby" + if: success() uses: "ruby/setup-ruby@v1" with: ruby-version: 2.7 - name: "bundle lock" + if: success() run: bundle lock - uses: "actions/setup-java@v4" + if: success() with: distribution: "temurin" java-version: "17" - name: "download" + if: success() run: curl -o wss-unified-agent.jar https://unified-agent.s3.amazonaws.com/wss-unified-agent.jar - name: "scan" + if: success() run: java -jar wss-unified-agent.jar env: - WS_APIKEY: ${{ secrets.MEND_API_KEY }} + WS_APIKEY: ${{ env.MEND_API_KEY }} WS_WSS_URL: https://saas-eu.whitesourcesoftware.com/agent - WS_USERKEY: ${{ secrets.MEND_TOKEN }} - WS_PRODUCTNAME: "content-and-tooling" - WS_PROJECTNAME: ${{ github.event.repository.name }} + WS_USERKEY: ${{ env.MEND_TOKEN }} + WS_PRODUCTNAME: ${{ env.PRODUCT_NAME }} + WS_PROJECTNAME: ${{ github.event.repository.name }} diff --git a/.github/workflows/module_acceptance.yml b/.github/workflows/module_acceptance.yml index ca9c246..08de0ae 100644 --- a/.github/workflows/module_acceptance.yml +++ b/.github/workflows/module_acceptance.yml @@ -14,7 +14,14 @@ on: required: false default: '' type: "string" - + kernel_modules: + description: "Volume map host kernel /lib/modules into docker container" + default: true + type: boolean + disable_apparmor: + description: "Disable and stop apparmor" + default: false + type: boolean jobs: @@ -68,6 +75,16 @@ jobs: - name: "Checkout" uses: "actions/checkout@v4" + - name: "Disable Apparmor" + if: ${{ inputs.disable_apparmor }} + run: | + if command -v apparmor_parser >/dev/null ; then + sudo find /etc/apparmor.d/ -maxdepth 1 -type f -exec ln -sf {} /etc/apparmor.d/disable/ \; + sudo apparmor_parser -R /etc/apparmor.d/disable/* || true + sudo systemctl disable apparmor + sudo systemctl stop apparmor + fi + - name: "Setup ruby" uses: "ruby/setup-ruby@v1" with: @@ -82,7 +99,7 @@ jobs: - name: "Provision environment" run: | - if [[ "${{matrix.platforms.provider}}" == "docker" ]]; then + if [[ "${{ inputs.kernel_modules }}" == "true" ]] && [[ "${{matrix.platforms.provider}}" =~ docker* ]] ; then DOCKER_RUN_OPTS="docker_run_opts: {'--volume': '/lib/modules/$(uname -r):/lib/modules/$(uname -r)'}" else DOCKER_RUN_OPTS='' diff --git a/.github/workflows/module_release.yml b/.github/workflows/module_release.yml index 98620f2..0879288 100644 --- a/.github/workflows/module_release.yml +++ b/.github/workflows/module_release.yml @@ -5,47 +5,139 @@ name: "Module Release" on: workflow_call: + inputs: + tag: + description: "Enter an old tag, or blank to tag HEAD of branch" + default: '' + type: string + release: + description: "Create a release on Github" + type: boolean + default: true + publish: + description: "Publish to forge.puppet.com" + type: boolean + default: true + edit: + description: "Re-tag and regenerate release notes" + type: boolean + default: false + +env: + FORGE_API_KEY: ${{ secrets.FORGE_API_KEY }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} jobs: + check: + runs-on: "ubuntu-latest" + steps: + - name: "Check Requirements" + if: ${{ inputs.publish == true || inputs.publish == 'true' }} + run: | + if [[ -z "${FORGE_API_KEY}" ]] ; then + echo "::error::missing required secret: FORGE_API_KEY" + exit 1 + fi + release: - name: "Release" + name: ${{ inputs.tag != '' && inputs.tag || 'new' }} + needs: check runs-on: "ubuntu-latest" - if: github.repository_owner == 'puppetlabs' steps: - - name: "Checkout" uses: "actions/checkout@v4" with: ref: "${{ github.ref }}" clean: true fetch-depth: 0 + fetch-tags: true + + - name: "Checkout tag ${{ inputs.tag }}" + if: ${{ inputs.tag != '' }} + run: | + git checkout refs/tags/${{ inputs.tag }} - - name: "Get version" - id: "get_version" + - name: "Get metadata" + id: metadata run: | - echo "version=$(jq --raw-output .version metadata.json)" >> $GITHUB_OUTPUT + metadata_version=$(jq --raw-output .version metadata.json) + if [[ -n "${{ inputs.tag }}" ]] ; then + tag=${{ inputs.tag }} + if [[ "${metadata_version}" != "${tag/v}" ]] ; then + echo "::error::tag ${tag/v} does not match metadata version ${metadata_version}" + exit 1 + fi + else + tag="v${metadata_version}" + fi + echo "tag=${tag}" >> $GITHUB_OUTPUT + echo "version=${metadata_version}" >> $GITHUB_OUTPUT - - name: "PDK build" + - name: "PDK build ${{ steps.metadata.outputs.version }}" uses: "docker://puppet/pdk:3.0.0.0" with: args: "build" - - name: "Generate release notes" + - name: "Generate release notes for Github" + continue-on-error: true run: | export GH_HOST=github.com gh extension install chelnak/gh-changelog - gh changelog get --latest > OUTPUT.md - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # TODO replace sed when gh-changelog supports templates + gh changelog get --latest | \ + sed -e "1,/^\[Full Changelog\]/ d" \ + -e 's/(\[\([^]]*\)\]([^)]*))$/@\1/g' \ + -e 's/\[#\([0-9]*\)\]([^)]*)/#\1/g' > OUTPUT.md + echo "::group::release notes" + cat OUTPUT.md + echo "::endgroup::" + + - name: "Tag ${{ steps.metadata.outputs.tag }}" + id: tag + run: | + # create an annotated tag -- gh release create DOES NOT do this for us! + # TODO move this to an automatic action when a release_prep PR is merged + git config --local user.email "${{ github.repository_owner }}@users.noreply.github.com" + git config --local user.name "GitHub Actions" + + # overwrite existing tag? + if [[ -n "${{ inputs.tag }}" ]] ; then + if [[ "${{ inputs.edit }}" == "true" ]] ; then + arg="-f" + else + skip_tag=1 + fi + fi + + if [[ -z "${skip_tag}" ]] ; then + GIT_COMMITTER_DATE="$(git log --format=%aD ...HEAD^)" git tag -a $arg -F OUTPUT.md "${{ steps.metadata.outputs.tag }}" + git push $arg origin tag "${{ steps.metadata.outputs.tag }}" + fi + + if gh release view "${{ steps.metadata.outputs.tag }}" > /dev/null ; then + echo "release_action=edit" >> $GITHUB_OUTPUT + echo "undraft=${{ inputs.edit }}" >> $GITHUB_OUTPUT + else + echo "release_action=create" >> $GITHUB_OUTPUT + fi + + # is latest tag? + LAST_TAG=$(git for-each-ref refs/tags --sort='-*creatordate' --format='%(refname:short)' --count=1) + if [[ "${LAST_TAG}" == "${{ steps.metadata.outputs.tag }}" ]] ; then + echo "latest=true" >> $GITHUB_OUTPUT + else + echo "latest=false" >> $GITHUB_OUTPUT + fi - - name: "Create release" + - name: "${{ steps.tag.outputs.release_action }} release for ${{ steps.metadata.outputs.tag }}" + if: ${{ inputs.release == true || inputs.release == 'true' || steps.tag.outputs.undraft == 'true' }} run: | - gh release create v${{ steps.get_version.outputs.version }} --title v${{ steps.get_version.outputs.version }} -F OUTPUT.md - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + gh release ${{ steps.tag.outputs.release_action }} ${{ steps.metadata.outputs.tag }} --latest=${{ steps.tag.outputs.latest }} --draft=false --title ${{ steps.metadata.outputs.tag }} -F OUTPUT.md - name: "Publish module" + if: ${{ inputs.publish == true || inputs.publish == 'true' }} uses: "docker://puppet/pdk:3.0.0.0" with: - args: 'release publish --forge-token ${{ secrets.FORGE_API_KEY }} --force' + args: 'release publish --forge-token ${{ env.FORGE_API_KEY }} --force' diff --git a/.github/workflows/tooling_mend_ruby.yml b/.github/workflows/tooling_mend_ruby.yml index 4b5eb2c..62af817 100644 --- a/.github/workflows/tooling_mend_ruby.yml +++ b/.github/workflows/tooling_mend_ruby.yml @@ -4,14 +4,42 @@ name: mend on: workflow_call: + inputs: + api_key: + default: '' + type: string + token: + default: '' + type: string + product_name: + default: 'DevX' + type: string -jobs: +env: + MEND_API_KEY: ${{ secrets.MEND_API_KEY != '' && secrets.MEND_API_KEY || inputs.api_key }} + MEND_TOKEN: ${{ secrets.MEND_TOKEN != '' && secrets.MEND_TOKEN || inputs.token }} + PRODUCT_NAME: ${{ inputs.PRODUCT_NAME != '' && inputs.PRODUCT_NAME || inputs.product_name }} + REQUIRE_SECRETS: MEND_API_KEY MEND_TOKEN +jobs: mend: runs-on: "ubuntu-latest" + continue-on-error: ${{ contains(fromJson('["puppetlabs","puppet-toy-chest"]'), github.repository_owner) != true }} steps: + - name: "check requirements" + run: | + declare -a MISSING + for V in ${REQUIRE_SECRETS} ; do + [[ -z "${!V}" ]] && MISSING+=($V) + done + if [ ${#MISSING[@]} -gt 0 ] ; then + echo "::warning::missing required secrets: ${MISSING[@]}" + exit 1 + fi + # If we are on a PR, checkout the PR head sha, else checkout the default branch - name: "Set the checkout ref" + if: success() id: set_ref run: | if [[ "${{ github.event_name }}" == "pull_request_target" ]]; then @@ -21,32 +49,38 @@ jobs: fi - name: "checkout" + if: success() uses: "actions/checkout@v4" with: fetch-depth: 1 ref: ${{ steps.set_ref.outputs.ref }} - name: "setup ruby" + if: success() uses: "ruby/setup-ruby@v1" with: ruby-version: 2.7 - name: "bundle lock" + if: success() run: bundle lock - uses: "actions/setup-java@v4" + if: success() with: distribution: "temurin" java-version: "17" - name: "download" + if: success() run: curl -o wss-unified-agent.jar https://unified-agent.s3.amazonaws.com/wss-unified-agent.jar - name: "scan" + if: success() run: java -jar wss-unified-agent.jar env: - WS_APIKEY: ${{ secrets.MEND_API_KEY }} + WS_APIKEY: ${{ env.MEND_API_KEY }} WS_WSS_URL: https://saas-eu.whitesourcesoftware.com/agent - WS_USERKEY: ${{ secrets.MEND_TOKEN }} - WS_PRODUCTNAME: "DevX" - WS_PROJECTNAME: ${{ github.event.repository.name }} + WS_USERKEY: ${{ env.MEND_TOKEN }} + WS_PRODUCTNAME: ${{ env.PRODUCT_NAME }} + WS_PROJECTNAME: ${{ github.event.repository.name }} diff --git a/example/module/ci.yml b/example/module/ci.yml new file mode 100644 index 0000000..6fada4a --- /dev/null +++ b/example/module/ci.yml @@ -0,0 +1,21 @@ +name: "CI" + +on: + pull_request: + branches: + - "main" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + Spec: + uses: "puppetlabs/cat-github-actions/.github/workflows/module_ci.yml@main" + secrets: "inherit" + + Acceptance: + needs: Spec + uses: "puppetlabs/cat-github-actions/.github/workflows/module_acceptance.yml@main" + secrets: "inherit" diff --git a/example/module/labeller.yml b/example/module/labeller.yml new file mode 100644 index 0000000..8dd3f3a --- /dev/null +++ b/example/module/labeller.yml @@ -0,0 +1,13 @@ +name: Labeller + +on: + issues: + types: [ opened, reopened, labeled, unlabeled ] + pull_request_target: + types: [ opened, reopened, labeled, unlabeled ] + +jobs: + label: + if: contains(fromJson('["puppetlabs", "puppet-toy-chest"]'), github.repository_owner) + uses: "puppetlabs/cat-github-actions/.github/workflows/labeller.yml@main" + secrets: inherit diff --git a/example/module/mend.yml b/example/module/mend.yml new file mode 100644 index 0000000..8b5b401 --- /dev/null +++ b/example/module/mend.yml @@ -0,0 +1,16 @@ +name: "mend" + +on: + pull_request_target: + types: + - opened + - synchronize + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + + mend: + uses: "puppetlabs/cat-github-actions/.github/workflows/mend_ruby.yml@main" + secrets: "inherit" diff --git a/example/module/nightly.yml b/example/module/nightly.yml new file mode 100644 index 0000000..1b06c47 --- /dev/null +++ b/example/module/nightly.yml @@ -0,0 +1,16 @@ +name: "nightly" + +on: + schedule: + - cron: "0 0 * * *" + workflow_dispatch: + +jobs: + Spec: + uses: "puppetlabs/cat-github-actions/.github/workflows/module_ci.yml@main" + secrets: "inherit" + + Acceptance: + needs: Spec + uses: "puppetlabs/cat-github-actions/.github/workflows/module_acceptance.yml@main" + secrets: "inherit" diff --git a/example/module/release.yml b/example/module/release.yml new file mode 100644 index 0000000..2bd5a73 --- /dev/null +++ b/example/module/release.yml @@ -0,0 +1,36 @@ +name: "Publish module" +run-name: > + ${{ format('tag={0}', inputs.tag) }} + ${{ format('release={0}', inputs.release) }} + ${{ format('publish={0}', inputs.publish) }} + ${{ format('edit={0}', inputs.edit) }} + +on: + workflow_dispatch: + inputs: + tag: + description: "Leave blank to tag HEAD of branch, or existing tag to edit" + default: '' + type: string + release: + description: "Create a Github release" + type: boolean + default: true + publish: + description: "Publish to the Forge" + type: boolean + default: true + edit: + description: "Regenerate release notes and existing tag" + default: false + type: boolean + +jobs: + release: + uses: "puppetlabs/cat-github-actions/.github/workflows/module_release.yml@forked-modules" + secrets: "inherit" + with: + tag: ${{ inputs.tag }} + release: ${{ inputs.release }} + publish: ${{ inputs.publish }} + edit: ${{ inputs.edit }} diff --git a/example/module/release_prep.yml b/example/module/release_prep.yml new file mode 100644 index 0000000..7f7bf45 --- /dev/null +++ b/example/module/release_prep.yml @@ -0,0 +1,17 @@ +name: "Release Prep" +run-name: > + version=${{ inputs.version }} + +on: + workflow_dispatch: + inputs: + version: + description: "Module version to be released. Must be a valid semver string. (1.2.3)" + required: true + +jobs: + release_prep: + uses: "puppetlabs/cat-github-actions/.github/workflows/module_release_prep.yml@main" + with: + version: "${{ inputs.version }}" + secrets: "inherit"