Skip to content
Merged
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
26 changes: 20 additions & 6 deletions .github/workflows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,20 +210,34 @@ It is composed of three jobs:

## [Release and publish](release.yml)

TODO
This workflow runs when a **pull request targeting *main* is closed**, and cuts a release when that PR was merged and its title is a version number.

## [Reusable release and tag workflow](_release_and_tag.yml)
It is composed of two jobs:

TODO
1. **release** - Checks whether the PR title matches `X.Y.Z`. If it does, the tag and the GitHub release are created by [**softprops/action-gh-release**](https://github.com/softprops/action-gh-release), with release notes generated automatically. The job exports the outcome as the `match` and `version` outputs.
2. **publish-image** - If `match` is true, calls the [**docker_publish**](docker_publish.yml) workflow with the new version as both `ref` and `version`, so the version-tagged images are built from the tag that was just created.

## [Build and publish Docker images](docker_publish.yml)

This workflow builds the `greedybear` and `greedybear_nginx` images and publishes them to the **GitHub Container Registry (GHCR)** under `ghcr.io/greedybear-project`, decoupling image hosting from the IntelOwl-owned DockerHub account.
This workflow builds the `greedybear` and `greedybear_nginx` images and publishes them to the **GitHub Container Registry (GHCR)** under `ghcr.io/greedybear-project`.

It runs on:

* pushes to **main** — tagged `prod`
* pushes to **develop** — tagged `stag`
* pushes of a **`X.Y.Z` git tag** — tagged with that version
* a call from [**release**](release.yml) — tagged with the `version` input, and also `prod` (see below)
* a manual `workflow_dispatch` — tagged with the version in the ref, when dispatched against a `X.Y.Z` tag

Each image is built via a job matrix and pushed by [**docker/build-push-action**](https://github.com/docker/build-push-action), with tags/labels derived by [**docker/metadata-action**](https://github.com/docker/metadata-action) and layer caching backed by the GitHub Actions cache.
There is deliberately **no `tags:` filter**: release tags are created with the automatic `GITHUB_TOKEN`, whose events do not start workflow runs, so such a filter could never match. Version builds arrive as a `workflow_call` instead. To rebuild the images for an existing tag, dispatch this workflow against that tag.

On a `workflow_call` from **release**, `github.ref` is `refs/heads/main` — the caller's branch, not the tag. Three things follow, all verified against a live run:

* `type=semver` does not fire, because the ref is not a tag. The `version` input is what produces the `X.Y.Z` tag, and the `ref` input is what gets checked out. `type=semver` is still required for the `workflow_dispatch` path, where the ref *is* the tag.
* `prod` **is** applied on a release call, since its `enable` condition tests exactly that ref. That is harmless — the tag points at the same commit as `main`, so both builds have identical input.
* The concurrency group must be a literal string rather than `github.workflow`, which in a called workflow resolves to the *caller's* workflow name and would cancel the called job before it could start.

### Caching

Merging a release PR starts **two** builds of the same commit: the push to `main` and the call from **release**. Only the branch builds export to the GitHub Actions cache; release builds set `cache-to` to an empty string and read the cache only. Letting both export to the same scope makes the second fail with `failed to reserve cache`, which cancels the image push it had already started — so the release would produce a tag and a GitHub release but no version-tagged image. The release build is rebuilding an already-cached commit, so it has nothing to contribute anyway.

Each image is built via a job matrix and pushed by [**docker/build-push-action**](https://github.com/docker/build-push-action), with tags/labels derived by [**docker/metadata-action**](https://github.com/docker/metadata-action).
186 changes: 0 additions & 186 deletions .github/workflows/_release_and_tag.yml

This file was deleted.

31 changes: 22 additions & 9 deletions .github/workflows/docker_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,26 @@ name: Build and publish Docker images

on:
push:
# Version builds arrive as a workflow_call from release.yml, not as a tag push
branches:
- main
- develop
tags:
- "[0-9]+.[0-9]+.[0-9]+"
workflow_dispatch:
workflow_call:
inputs:
ref:
description: Git ref to build. Defaults to the triggering ref.
type: string
required: false
version:
description: Semver version to tag the image with, e.g. 1.2.3.
type: string
required: false

# cancel superseded runs for the same ref
# Cancel superseded runs for the same ref.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
group: docker-publish-${{ inputs.ref || github.ref }}
cancel-in-progress: ${{ inputs.version == '' }}

env:
GHCR_NAMESPACE: ghcr.io/greedybear-project
Expand All @@ -38,6 +47,8 @@ jobs:
steps:
- name: Check out repository
uses: actions/checkout@v7
with:
ref: ${{ inputs.ref || github.ref }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
Expand All @@ -54,13 +65,15 @@ jobs:
uses: docker/metadata-action@v6
with:
images: ${{ env.GHCR_NAMESPACE }}/${{ matrix.image }}
# main branch -> prod
# develop branch -> stag
# X.Y.Z git tag -> X.Y.Z
# main branch -> prod
# develop branch -> stag
# dispatch on a tag -> X.Y.Z (from the ref)
# release.yml call -> X.Y.Z (from the version input)
tags: |
type=raw,value=prod,enable=${{ github.ref == 'refs/heads/main' }}
type=raw,value=stag,enable=${{ github.ref == 'refs/heads/develop' }}
type=semver,pattern={{version}}
type=raw,value=${{ inputs.version }},enable=${{ inputs.version != '' }}
flavor: |
latest=false

Expand All @@ -73,4 +86,4 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha,scope=${{ matrix.image }}
cache-to: type=gha,mode=max,scope=${{ matrix.image }}
cache-to: ${{ inputs.version == '' && format('type=gha,mode=max,scope={0}', matrix.image) || '' }}
68 changes: 53 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,66 @@
name: Release and publish

# Creates the X.Y.Z release and tag when a PR titled with a version number is
# merged into main, then builds and publishes the version-tagged Docker images.

on:
pull_request:
types:
- closed
branches:
- main

# discard previous execution if you commit to a branch that is already running
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
release:
name: Create release and tag
runs-on: ubuntu-latest
if: github.event.pull_request.merged == true
permissions:
contents: write
outputs:
match: ${{ steps.check-tag.outputs.match }}
version: ${{ steps.check-tag.outputs.version }}
steps:
- uses: actions/checkout@v4

- name: Check version tag format
id: check-tag
env:
# via the environment, so a crafted PR title cannot inject shell code
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
if [[ "$PR_TITLE" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "match=true" >> "$GITHUB_OUTPUT"
echo "version=$PR_TITLE" >> "$GITHUB_OUTPUT"
echo "PR title matches semver: $PR_TITLE"
else
echo "match=false" >> "$GITHUB_OUTPUT"
echo "PR title does not match semver - skipping release"
fi

- name: Create release
if: steps.check-tag.outputs.match == 'true'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.check-tag.outputs.version }}
name: Version ${{ steps.check-tag.outputs.version }}
draft: false
generate_release_notes: true
prerelease: false
target_commitish: ${{ github.base_ref }}
append_body: true

release_and_tag:
uses: ./.github/workflows/_release_and_tag.yml
secrets: inherit
publish-image:
name: Publish Docker images
needs: release
if: needs.release.outputs.match == 'true'
permissions:
contents: read
packages: write
uses: ./.github/workflows/docker_publish.yml
with:
publish_on_pypi: false
publish_on_test_pypi: false
publish_on_npm: false
publish_on_twitter: false
publish_on_ecr: false
repository: certego-test
working_directory: .github/test/python_test
dockerfiles: >-
["Dockerfile"]
aws_region: eu-central-1
ref: ${{ needs.release.outputs.version }}
version: ${{ needs.release.outputs.version }}
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "greedybear"
version = "3.6.0"
version = "3.6.1"
description = "Threat intelligence platform that extracts attack data from a T-Pot or a cluster of them and generates actionable live feeds."
readme = "README.md"
license = "MIT"
Expand Down Expand Up @@ -51,7 +51,7 @@ test = [
"django-test-migrations==1.6.0",
]
lint = [
"ruff==0.16.4",
"ruff==0.16.5",
]

[tool.uv]
Expand Down
Loading
Loading