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
129 changes: 106 additions & 23 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,9 @@ jobs:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json

publish:
name: Build & Publish Release
# Builds, publishes nothing. Everything public waits behind security-gate.
build:
name: Build Release Artifacts
needs: release-please
# always() because release-please is skipped on workflow_dispatch, and a
# skipped dependency would otherwise skip this job too.
Expand All @@ -60,9 +61,13 @@ jobs:
github.event_name == 'workflow_dispatch')
runs-on: ubuntu-latest
permissions:
contents: write # to upload assets to the GitHub release
contents: read
outputs:
version: ${{ steps.resolve.outputs.version }}
ref: ${{ steps.resolve.outputs.ref }}
env:
TAG_NAME: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || needs.release-please.outputs.tag_name }}
BUILD_REF: ${{ github.event_name == 'workflow_dispatch' && (inputs.ref || inputs.tag) || needs.release-please.outputs.tag_name }}
steps:
- name: Checkout
uses: actions/checkout@v7
Expand All @@ -79,6 +84,7 @@ jobs:
exit 1
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "ref=$BUILD_REF" >> "$GITHUB_OUTPUT"

- name: Validate Gradle Wrapper
uses: gradle/actions/wrapper-validation@v6
Expand All @@ -95,6 +101,76 @@ jobs:
- name: Build
run: ./gradlew check shadowJar cyclonedxBom

- name: Upload plugin JAR
uses: actions/upload-artifact@v7
with:
name: plugin-jar
path: build/libs/AntiRedstoneClock-Remastered-*.jar
if-no-files-found: error
retention-days: 14

- name: Upload SBOM artifact
uses: actions/upload-artifact@v7
with:
name: sbom
path: build/reports/cyclonedx/bom.xml
if-no-files-found: error
retention-days: 14

- name: Upload build reports
if: always()
uses: actions/upload-artifact@v7
with:
name: publish-reports
path: build/reports/**
if-no-files-found: ignore
retention-days: 14

# The point of no return. Everything below this job is public, so a HIGH or
# CRITICAL finding in the jar we are about to ship stops the release here.
# Scans the built artifact, not the source tree: for a shaded jar that is the
# only target that sees what actually ships.
security-gate:
name: Security Gate
needs: build
permissions:
contents: read
security-events: write # SARIF upload to code scanning
uses: OneLiteFeatherNET/workflows/.github/workflows/security-scan.yml@v2.6.0
with:
scan-type: rootfs
artifact-name: plugin-jar
severity: "CRITICAL,HIGH"
fail-on-findings: true
secrets: inherit

publish:
name: Publish Release
needs: [release-please, build, security-gate]
# No always() here - a failed gate must stop the release, and always()
# would run this job anyway.
if: >-
!cancelled() &&
needs.security-gate.result == 'success' &&
(needs.release-please.outputs.release_created == 'true' ||
github.event_name == 'workflow_dispatch')
runs-on: ubuntu-latest
permissions:
contents: write # to upload assets to the GitHub release
env:
TAG_NAME: ${{ github.event_name == 'workflow_dispatch' && inputs.tag || needs.release-please.outputs.tag_name }}
steps:
- name: Checkout
uses: actions/checkout@v7
with:
ref: ${{ needs.build.outputs.ref }}

- name: Download plugin JAR
uses: actions/download-artifact@v4
with:
name: plugin-jar
path: build/libs

# Deliberately before the platform uploads. Hangar and Modrinth reject
# versions for reasons outside this repository's control; when that
# happens the artifact should still be attached to the GitHub release
Expand All @@ -104,29 +180,36 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release upload "$TAG_NAME" build/libs/AntiRedstoneClock-Remastered-*.jar --clobber

- name: Setup Java
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: 24

- name: Setup Gradle
uses: gradle/actions/setup-gradle@v6

- name: Publish to Hangar and Modrinth
env:
HANGAR_SECRET: ${{ secrets.HANGAR_KEY }}
MODRINTH_TOKEN: ${{ secrets.MODRINTH_KEY }}
run: ./gradlew publishAllPublicationsToHangar modrinth

- name: Upload BOM to Dependency-Track
uses: DependencyTrack/gh-upload-sbom@v4
with:
serverhostname: ${{ secrets.DEPENDENCYTRACK_HOSTNAME }}
apikey: ${{ secrets.DEPENDENCYTRACK_APIKEY }}
projectname: "AntiRedstoneClock-Remastered"
projectversion: ${{ steps.resolve.outputs.version }}
projecttags: 'bukkit,paper,plugin'
bomfilename: "build/reports/cyclonedx/bom.xml"
autocreate: true
parent: '682857a9-0cd2-4ffd-a2b3-098eeba5ab74'

- name: Upload build reports
if: always()
uses: actions/upload-artifact@v7
with:
name: publish-reports
path: build/reports/**
if-no-files-found: ignore
retention-days: 14
# Separate job so Dependency-Track being down, or rejecting the API key,
# cannot take the release with it. 2.9.0 is why: the SBOM upload sat inside
# the publish job and one rejected platform version cost the JAR, both
# platform releases and the SBOM at once.
sbom:
name: Publish SBOM
needs: [build, publish]
uses: OneLiteFeatherNET/workflows/.github/workflows/sbom-publish.yml@v2.6.0
with:
project-name: "AntiRedstoneClock-Remastered"
project-version: ${{ needs.build.outputs.version }}
project-tags: "bukkit,paper,plugin"
parent-uuid: "682857a9-0cd2-4ffd-a2b3-098eeba5ab74"
# The CycloneDX Gradle plugin resolves the dependency graph better than
# an external scanner, so hand it the artifact the build produced.
artifact-name: "sbom"
sbom-path: "bom.xml"
secrets: inherit
36 changes: 36 additions & 0 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Security

# Dependency and secret scanning via the shared Trivy workflow. Complements
# CodeQL rather than duplicating it: CodeQL analyses our own code, Trivy looks
# at what we pull in and at anything credential-shaped that slipped into the
# tree.
#
# No pull_request trigger on purpose. Fork pull requests get a read-only token,
# so the SARIF upload to code scanning would fail for exactly the contributions
# that need review most. Scanning main plus a weekly sweep also catches CVEs
# published against code that has not changed - which is the majority of them.

"on":
push:
branches:
- main
schedule:
- cron: '0 6 * * 1'
workflow_dispatch:

permissions:
contents: read

jobs:
trivy:
name: Trivy
permissions:
contents: read
security-events: write # to upload the SARIF report to code scanning
uses: OneLiteFeatherNET/workflows/.github/workflows/security-scan.yml@v2.6.0
with:
scan-type: fs
severity: "CRITICAL,HIGH"
# Report-only for now. Flip once the backlog is at zero, otherwise the
# first unrelated CVE blocks main.
fail-on-findings: false
10 changes: 10 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,16 @@ allprojects {
}

dependencies {
constraints {
// CVE-2023-5072 (HIGH): org.json below 20231013 can be driven into a
// parser denial of service. It reaches the shaded jar transitively via
// club.minnced:discord-webhooks, which still resolves 20230618.
// Found by the Trivy gate in .github/workflows/release-please.yml.
implementation("org.json:json:20231013") {
because("CVE-2023-5072 in the version discord-webhooks pulls in")
}
}

implementation(libs.bstats)
implementation(libs.cloud.command.paper)
implementation(libs.cloud.command.extras)
Expand Down