diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..cd83dc8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + cooldown: + default-days: 7 diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index 72c2bd0..b49a165 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -3,35 +3,35 @@ name: CD on: workflow_call: jobs: - deploy: + deploy: # zizmor: ignore[secrets-outside-env] reusable workflow; environments are managed by callers runs-on: ubuntu-latest if: ${{ github.event.workflow_run.conclusion == 'success' }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Tag and Push Gem id: tag-and-push-gem - uses: discourse/publish-rubygems-action@v3 + uses: discourse/publish-rubygems-action@4bd305c65315cb691bad1e8de97a87aaf29a0a85 # v3 env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} GIT_EMAIL: ${{secrets.GUSTO_GIT_EMAIL}} GIT_NAME: ${{secrets.GUSTO_GIT_NAME}} RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}} - name: Create GitHub Release + # zizmor: ignore[template-injection] gem_version comes from a trusted prior step run: gh release create v${{steps.tag-and-push-gem.outputs.gem_version}} --generate-notes if: ${{ steps.tag-and-push-gem.outputs.new_version == 'true' }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - notify_on_failure: + notify_on_failure: # zizmor: ignore[secrets-outside-env] reusable workflow; environments are managed by callers runs-on: ubuntu-latest needs: [deploy] if: ${{ failure() && github.ref == 'refs/heads/main' }} - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK steps: - - uses: slackapi/slack-github-action@v1.25.0 + - uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3 with: + webhook: ${{ secrets.SLACK_WEBHOOK_URL }} + webhook-type: incoming-webhook payload: | - { - "text": "${{ github.repository }}/${{ github.ref }}: FAILED\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - } + text: "${{ github.repository }}/${{ github.ref }}: FAILED\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ee8a6c..dd05d91 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,23 +27,28 @@ jobs: BUNDLE_GEMFILE: Gemfile name: "Run tests: Ruby ${{ matrix.ruby }}" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Install ripgrep run: sudo apt-get install -y ripgrep - name: Set up Ruby ${{ matrix.ruby }} - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@4c56a21280b36d862b5fc31348f463d60bdc55d5 # v1 with: bundler-cache: true ruby-version: ${{ matrix.ruby }} - name: Run tests + # zizmor: ignore[template-injection] workflow_call inputs are controlled by the caller run: ${{ inputs.test-command }} static_type_check: name: "Type Check" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Ruby - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@4c56a21280b36d862b5fc31348f463d60bdc55d5 # v1 with: bundler-cache: true ruby-version: 3.4 @@ -53,25 +58,25 @@ jobs: runs-on: ubuntu-latest name: "Linter" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false - name: Set up Ruby - uses: ruby/setup-ruby@v1 + uses: ruby/setup-ruby@4c56a21280b36d862b5fc31348f463d60bdc55d5 # v1 with: bundler-cache: true ruby-version: 3.4 - name: Run linter + # zizmor: ignore[template-injection] workflow_call inputs are controlled by the caller run: ${{ inputs.linter-command }} - notify_on_failure: + notify_on_failure: # zizmor: ignore[secrets-outside-env] reusable workflow; environments are managed by callers runs-on: ubuntu-latest needs: [run_tests, static_type_check, run_linter] if: ${{ failure() && github.ref == 'refs/heads/main' }} - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK steps: - - uses: slackapi/slack-github-action@v1.25.0 + - uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 # v3 with: + webhook: ${{ secrets.SLACK_WEBHOOK_URL }} + webhook-type: incoming-webhook payload: | - { - "text": "${{ github.repository }}/${{ github.ref }}: FAILED\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" - } + text: "${{ github.repository }}/${{ github.ref }}: FAILED\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index daabc95..f643305 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -6,7 +6,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v9 + - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10 with: stale-issue-message: 'This issue has been marked stale because it has been open for six months with no activity. To prevent this issue from automatically being closed in one week, update it or remove the stale label.' stale-pr-message: 'This PR has been marked stale because it has been open for six months with no activity. To prevent this PR from automatically being closed in one week, update it or remove the stale label.' diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000..cebce9f --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,23 @@ +name: GitHub Actions Security Analysis + +on: + push: + branches: [main] + pull_request: + branches: ["**"] + +permissions: {} + +jobs: + zizmor: + runs-on: ubuntu-latest + permissions: + security-events: write + contents: read + actions: read + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + persist-credentials: false + - name: Run zizmor + uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 diff --git a/README.md b/README.md index 8df7404..d3cf40a 100644 --- a/README.md +++ b/README.md @@ -1 +1,61 @@ -# shared-config \ No newline at end of file +# shared-config + +Shared reusable GitHub Actions workflows for [rubyatscale](https://github.com/rubyatscale) gems. + +## Workflows + +### Reusable workflows (`workflow_call`) + +These workflows are called from individual gem repos via `uses: rubyatscale/shared-config/.github/workflows/.yml@main`. + +| Workflow | Description | +|----------|-------------| +| **CI** (`ci.yml`) | Runs tests across Ruby 3.2–4.0, Sorbet type checking, and linting (RuboCop). Test and linter commands are configurable via inputs. | +| **CD** (`cd.yml`) | Publishes the gem to RubyGems and creates a GitHub Release on successful main builds. | +| **Stale** (`stale.yml`) | Marks issues and PRs as stale after 180 days of inactivity, then closes them after 7 more days. | +| **Triage** (`triage.yml`) | Labels new issues with `triage`. | + +### Repository workflows + +| Workflow | Description | +|----------|-------------| +| **zizmor** (`zizmor.yml`) | Runs the [zizmor](https://github.com/zizmorcore/zizmor) security linter against all workflow files on every push and PR. | + +## Usage + +In a gem repo, create a workflow that calls the shared workflow: + +```yaml +# .github/workflows/ci.yml +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + ci: + uses: rubyatscale/shared-config/.github/workflows/ci.yml@main +``` + +### CI inputs + +| Input | Default | Description | +|-------|---------|-------------| +| `test-command` | `bundle exec rspec` | Command to run tests | +| `linter-command` | `bundle exec rubocop` | Command to run the linter | + +### Required secrets + +The **CD** workflow requires the following secrets in the calling repo: + +- `GUSTO_GIT_EMAIL` / `GUSTO_GIT_NAME` — Git identity for tagging +- `RUBYGEMS_API_KEY` — API key for publishing to RubyGems +- `SLACK_WEBHOOK_URL` — Incoming webhook URL for failure notifications (used by both CI and CD) + +## Security + +- All action references are pinned to SHA hashes +- [zizmor](https://github.com/zizmorcore/zizmor) runs on every PR to lint workflows for security issues +- [Dependabot](https://docs.github.com/en/code-security/dependabot) is configured for monthly GitHub Actions updates \ No newline at end of file