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
155 changes: 136 additions & 19 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,56 @@ name: Release
# replacing the previous fully-manual process (hand-bump .release.yml +
# ghascompliance/__version__.py, then hand-run `gh release create`).
#
# Two paths, both calling into the same reusable release tooling:
# - auto-release: fires when .release.yml's version changes on main (the
# normal release flow) - reuses python-release.yml's existing
# cooldown-aware version-vs-latest-release check, so it's a no-op if
# .release.yml changed without an actual version bump. Always a full
# release (never a pre-release), since a committed .release.yml bump
# represents a finalized version.
# - manual-release: workflow_dispatch for on-demand releases, including
# pre-releases (e.g. a beta cut ahead of finalizing .release.yml).
# Modeled on advanced-security/spdx-dependency-submission-action's release.yml
# (which itself works around a patch-release-me bug - see the bump-version
# job below). Three paths:
#
# - bump-version: workflow_dispatch with "bump" (patch/minor/major) - opens
# a PR that bumps .release.yml and every location it tracks (README.md,
# ghascompliance/__version__.py) via patch-release-me. This is the
# "normal" way to cut a release: merging that PR pushes a .release.yml
# change to main, which triggers auto-release below. Does not itself
# create a GitHub Release.
# - auto-release: fires when .release.yml's version changes on main (e.g.
# the bump-version PR above gets merged) - reuses python-release.yml's
# existing cooldown-aware version-vs-latest-release check, so it's a
# no-op if .release.yml changed without an actual version bump (e.g. a
# locations-only edit). Always a full release (never a pre-release),
# since a committed .release.yml bump represents a finalized version.
# - manual-release: workflow_dispatch with an explicit "version" - cuts a
# release immediately, bypassing .release.yml entirely. For ad hoc
# releases and pre-releases (e.g. a beta cut ahead of finalizing
# .release.yml). Does NOT bump .release.yml/README.md/__version__.py -
# follow up with a "bump" run (or manual edit) to keep them in sync.
#
# auto-release and manual-release (the two paths that actually publish a
# release) depend on check-vendor-sync first: action.yml runs the action
# directly out of the committed vendor/ tree (PYTHONPATH includes vendor/),
# so vendor/ IS the shipped runtime artifact, not just a repo nicety. If
# Pipfile.lock moved on main (e.g. a merged Dependabot PR) but the resulting
# Vendor Sync PR (vendor-sync.yml) hasn't been merged yet, vendor/ is stale -
# releasing in that window would ship mismatched dependencies.
# check-vendor-sync regenerates vendor/ and fails the run if that produces a
# diff, instead of releasing silently against a stale tree.
on:
push:
branches: [ main ]
paths:
- '.release.yml'
workflow_dispatch:
inputs:
version:
description: 'Explicit version to release (e.g. 2.12.0 or 2.12.0-beta.1). Leave blank to auto-bump from the latest release.'
required: false
type: string
bump:
description: 'Auto-bump type, used only when "version" above is left blank.'
description: 'Opens a PR bumping .release.yml (and README.md/ghascompliance/__version__.py). Ignored if "version" is set.'
required: false
type: choice
options: [patch, minor, major]
default: patch
options: [none, patch, minor, major]
default: none
version:
description: 'Explicit version to release right now (e.g. 2.12.0 or 2.12.0-beta.1), bypassing .release.yml. Takes priority over "bump".'
required: false
type: string
prerelease:
description: 'Publish as a pre-release (tags vX.Y.Z only; skips moving the floating vX/vX.Y tags and skips --latest).'
description: 'Publish as a pre-release (only used with "version"; tags vX.Y.Z only, skips moving the floating vX/vX.Y tags and skips --latest).'
required: false
type: boolean
default: false
Expand All @@ -41,16 +63,111 @@ permissions:
pull-requests: write

jobs:
# Opens a PR bumping .release.yml (+ tracked locations) - does not release.
bump-version:
if: ${{ github.event_name == 'workflow_dispatch' && inputs.version == '' && inputs.bump != 'none' && github.repository == 'advanced-security/policy-as-code' }}
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v7

# Invokes the pinned patch-release-me image directly via `docker run`
# instead of `uses: 42ByteLabs/patch-release-me@...`. That action's own
# action.yml builds its Docker CMD as a single YAML list item
# (`-m "${{ inputs.bump }}"`); container actions don't go through a
# shell, so the literal quote characters end up baked into ONE argv
# token (`-m "minor"`). clap can't match that against
# "patch"/"minor"/"major" and silently falls back to its own default
# (patch) - so `bump: minor`/`major` would always quietly produce a
# patch bump instead (see
# https://github.com/42ByteLabs/patch-release-me/issues/161, fixed
# upstream in https://github.com/42ByteLabs/patch-release-me/pull/162,
# not yet merged/released). advanced-security/reusable-workflows'
# own self-release.yml still uses the buggy wrapped-action form, so
# don't copy that pattern - a plain shell `run:` step naturally splits
# `-m` and the mode value into separate argv entries, avoiding the bug
# entirely. Pinned by digest (not just tag) so the exact image content
# can't change out from under us - digest corresponds to the 0.6.5 tag,
# the last one with a published image (0.6.6 has none - see
# https://github.com/42ByteLabs/patch-release-me/issues/159).
- name: Patch Release Me
run: |
set -euo pipefail
docker run --rm \
--user "$(id -u):$(id -g)" \
-v "${{ github.workspace }}:/repo" \
-w /repo \
ghcr.io/42bytelabs/patch-release-me@sha256:d9d7abe7051855d0c395fec99d931acc002fb6b299ca16b8123e2c8ef0c7e750 \
--disable-banner bump -m ${{ inputs.bump }}

- name: Create Release PR
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1
with:
token: ${{ github.token }}
commit-message: "chore: bump version (${{ inputs.bump }})"
title: "chore: bump version (${{ inputs.bump }})"
branch: chore/release-${{ inputs.bump }}
labels: version
body: |
Automated PR to bump the version tracked in `.release.yml`, and every location it
patches (`README.md`, `ghascompliance/__version__.py`).

Merging this to `main` automatically publishes a GitHub Release for the new version,
once `check-vendor-sync` confirms `vendor/` is still in sync with `Pipfile.lock`.

> [!WARNING]
> This PR was opened with the default `GITHUB_TOKEN`, so GitHub will **not**
> auto-trigger required status checks (`run (3.10)`, `run (3.11)`, `run (3.12)`,
> `run (3.13)`, `e2e-tests`). A maintainer needs to manually re-run them (or push an
> empty commit) before merging - see the `TODO(#94)` note in `vendor-sync.yml` for
> the long-term fix (same root cause).

check-vendor-sync:
if: >-
github.repository == 'advanced-security/policy-as-code' &&
(github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.version != ''))
runs-on: ubuntu-latest
Comment on lines +128 to +131
steps:
- uses: actions/checkout@v7

- uses: actions/setup-python@v7
with:
python-version: '3.11'

- name: Install pipenv
run: python -m pip install 'pip==26.0.1' 'pipenv==2025.0.4'

- name: Sync environment
run: pipenv sync --dev

# `pipenv run vendor` is a Pipfile script alias - see the [scripts]
# section in Pipfile: `vendor = "./vendor/update.sh"`. Same step
# vendor-sync.yml runs to regenerate vendor/ from Pipfile.lock.
- name: Regenerate vendor/ from Pipfile.lock (runs vendor/update.sh)
run: pipenv run vendor

- name: Fail if vendor/ is out of sync with Pipfile.lock
run: |
if git status --porcelain -- vendor | grep -q .; then
echo "::error::vendor/ is out of sync with Pipfile.lock - releasing now would ship a stale vendor/ tree."
git status --porcelain -- vendor
echo "Merge the pending 'chore/vendor-sync' PR (see .github/workflows/vendor-sync.yml) before releasing."
exit 1
fi

auto-release:
needs: check-vendor-sync
if: ${{ github.event_name == 'push' && github.repository == 'advanced-security/policy-as-code' }}
uses: advanced-security/reusable-workflows/.github/workflows/python-release.yml@v0.3.5
secrets: inherit

manual-release:
if: ${{ github.event_name == 'workflow_dispatch' && github.repository == 'advanced-security/policy-as-code' }}
needs: check-vendor-sync
if: ${{ github.event_name == 'workflow_dispatch' && inputs.version != '' && github.repository == 'advanced-security/policy-as-code' }}
uses: advanced-security/reusable-workflows/.github/workflows/release.yml@v0.3.5
with:
version: ${{ inputs.version }}
bump: ${{ inputs.bump }}
prerelease: ${{ inputs.prerelease }}
secrets: inherit
8 changes: 7 additions & 1 deletion .release.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: "policy-as-code"
repository: "advanced-security/policy-as-code"
version: "2.11.1"
version: "2.12.1"

ecosystems:
- Python
Expand All @@ -13,3 +13,9 @@ locations:
patterns:
- "{repository}@v{version}"
- '--branch "v{version}"'

- name: "Version Module"
paths:
- "ghascompliance/__version__.py"
patterns:
- '__version__ = "{version}"'
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Here is how you can quickly setup policy-as-code.
```yaml
# Policy as Code
- name: Advanced Security Policy as Code
uses: advanced-security/policy-as-code@v2.11.1
uses: advanced-security/policy-as-code@v2.12.1
```

#### Structured results
Expand All @@ -76,7 +76,7 @@ Set the `output` input to use a different file location.
```yaml
- name: Advanced Security Policy as Code
id: policy
uses: advanced-security/policy-as-code@v2.11.1
uses: advanced-security/policy-as-code@v2.12.1

- name: Read total violations
run: echo '${{ fromJSON(steps.policy.outputs.results).total_violations }}'
Expand Down Expand Up @@ -139,15 +139,15 @@ The Policy as Code project is a self-contained Python based CLI tool.
**Bash / Zsh:**

```bash
git clone --branch "v2.11.1" https://github.com/advanced-security/policy-as-code.git && cd ./policy-as-code
git clone --branch "v2.12.1" https://github.com/advanced-security/policy-as-code.git && cd ./policy-as-code

./policy-as-code --help
```

**Powershell:**

```Powershell
git clone --branch "v2.11.1" https://github.com/advanced-security/policy-as-code.git
git clone --branch "v2.12.1" https://github.com/advanced-security/policy-as-code.git
cd policy-as-code

.\policy-as-code.ps1 --help
Expand Down Expand Up @@ -218,7 +218,7 @@ Here is an example of using a simple yet cross-organization using Policy as Code
```yaml
# Compliance
- name: Advanced Security Policy as Code
uses: advanced-security/policy-as-code@v2.11.1
uses: advanced-security/policy-as-code@v2.12.1
with:
# The owner/repo of where the policy is stored
policy: GeekMasher/security-queries
Expand Down
6 changes: 3 additions & 3 deletions docs/introduction/actions.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ To do this, you can simply add the following to your workflow:
```yaml
# Compliance
- name: Advanced Security Compliance Action
uses: advanced-security/policy-as-code@v2.11.1
uses: advanced-security/policy-as-code@v2.12.1
```

This runs the policy-as-code action with the default configuration. You can also specify a configuration file to use:

```yaml
- name: Advanced Security Compliance Action
uses: advanced-security/policy-as-code@v2.11.1
uses: advanced-security/policy-as-code@v2.12.1
with:
policy: GeekMaherOrg/security
policy-branch: main
Expand All @@ -32,5 +32,5 @@ Policy as Code is written in Python, so you will need to setup Python in your wo
python-version: '3.10' # minimum supported Python version

- name: Advanced Security Compliance Action
uses: advanced-security/policy-as-code@v2.11.1
uses: advanced-security/policy-as-code@v2.12.1
```
2 changes: 1 addition & 1 deletion ghascompliance/__version__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python
__version__ = "2.11.1"
__version__ = "2.12.1"

__title__ = "GitHub Advanced Security Policy as Code"
__name__ = "ghascompliance"
Expand Down
Loading