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
1 change: 0 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ tmp
.golangci.yml
.goreleaser.yml
.vscode
docs
LICENSE
README.md
codecov.yml
120 changes: 120 additions & 0 deletions .github/actions/upload-artifact-resilient/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
# ------------------------------------------------------------------------------------
# Upload Artifact with Resilience (Composite Action) (GoFortress)
#
# Purpose: Provide resilient artifact uploads with step-level retry logic to handle
# transient GitHub infrastructure failures (including non-retryable 403 errors from
# CDN/proxy intermediaries during artifact finalization).
#
# This action handles:
# - Step-level retry (3 attempts) to recover from non-retryable errors (e.g., 403)
# - Escalating delays (10s, 30s) between retries for transient infrastructure issues
# - overwrite: true on all attempts to handle partially-finalized artifacts
# - ACTIONS_UPLOAD_RETRY_COUNT=3 for defense-in-depth against 5xx errors
# - Configurable continue-on-error for critical vs non-critical artifacts
#
# Maintainer: @mrz1836
#
# ------------------------------------------------------------------------------------

name: "Upload Artifact with Resilience"
description: "Uploads GitHub Actions artifacts with step-level retry logic for transient infrastructure failures"

inputs:
artifact-name:
description: "Name of the artifact (will be displayed in GitHub UI)"
required: true
artifact-path:
description: "Path to the artifact file(s) to upload"
required: true
retention-days:
description: "Number of days to retain the artifact (1-90 days)"
required: false
default: "7"
if-no-files-found:
description: "Behavior when no files match the path (warn, error, ignore)"
required: false
default: "ignore"
compression-level:
description: "Compression level for the artifact (0-9, 6 is default)"
required: false
default: "6"
continue-on-error:
description: "Continue workflow if all upload attempts fail (true for non-critical artifacts)"
required: false
default: "true"

runs:
using: "composite"
steps:
# ------------------------------------------------------------------
# Attempt 1
# ------------------------------------------------------------------
- name: "📤 Upload ${{ inputs.artifact-name }} (attempt 1)"
id: attempt1
continue-on-error: true
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.artifact-path }}
retention-days: ${{ inputs.retention-days }}
if-no-files-found: ${{ inputs.if-no-files-found }}
compression-level: ${{ inputs.compression-level }}
overwrite: true
env:
ACTIONS_UPLOAD_RETRY_COUNT: 3

# ------------------------------------------------------------------
# Delay before retry
# ------------------------------------------------------------------
- name: "⏳ Wait before retry (${{ inputs.artifact-name }})"
if: steps.attempt1.outcome == 'failure'
shell: bash
run: |
echo "::warning::Upload attempt 1 for '${{ inputs.artifact-name }}' failed, retrying in 10s..."
sleep 10

# ------------------------------------------------------------------
# Attempt 2
# ------------------------------------------------------------------
- name: "📤 Upload ${{ inputs.artifact-name }} (attempt 2)"
id: attempt2
if: steps.attempt1.outcome == 'failure'
continue-on-error: true
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.artifact-path }}
retention-days: ${{ inputs.retention-days }}
if-no-files-found: ${{ inputs.if-no-files-found }}
compression-level: ${{ inputs.compression-level }}
overwrite: true
env:
ACTIONS_UPLOAD_RETRY_COUNT: 3

# ------------------------------------------------------------------
# Delay before final retry
# ------------------------------------------------------------------
- name: "⏳ Wait before final retry (${{ inputs.artifact-name }})"
if: steps.attempt1.outcome == 'failure' && steps.attempt2.outcome == 'failure'
shell: bash
run: |
echo "::warning::Upload attempt 2 for '${{ inputs.artifact-name }}' failed, retrying in 30s..."
sleep 30

# ------------------------------------------------------------------
# Attempt 3 (final -- continue-on-error depends on criticality input)
# ------------------------------------------------------------------
- name: "📤 Upload ${{ inputs.artifact-name }} (attempt 3 - final)"
id: attempt3
if: steps.attempt1.outcome == 'failure' && steps.attempt2.outcome == 'failure'
continue-on-error: ${{ inputs.continue-on-error == 'true' }}
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: ${{ inputs.artifact-name }}
path: ${{ inputs.artifact-path }}
retention-days: ${{ inputs.retention-days }}
if-no-files-found: ${{ inputs.if-no-files-found }}
compression-level: ${{ inputs.compression-level }}
overwrite: true
env:
ACTIONS_UPLOAD_RETRY_COUNT: 3
2 changes: 1 addition & 1 deletion .github/env/10-coverage.env
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ GO_COVERAGE_PROVIDER=internal
CODECOV_TOKEN_REQUIRED=false

# Go Coverage Tool Version
GO_COVERAGE_VERSION=v1.3.5
GO_COVERAGE_VERSION=v1.3.7
GO_COVERAGE_USE_LOCAL=false

# ================================================================================================
Expand Down
4 changes: 2 additions & 2 deletions .github/env/10-mage-x.env
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
# ================================================================================================

# MAGE-X version
MAGE_X_VERSION=v1.20.4
MAGE_X_VERSION=v1.20.7

# For mage-x development, set to 'true' to use local version instead of downloading from releases
MAGE_X_USE_LOCAL=false
Expand All @@ -61,7 +61,7 @@ MAGE_X_FORMAT_EXCLUDE_PATHS=vendor,node_modules,.git,.idea

MAGE_X_GITLEAKS_VERSION=8.30.0
MAGE_X_GOFUMPT_VERSION=v0.9.2
MAGE_X_GOLANGCI_LINT_VERSION=v2.9.0
MAGE_X_GOLANGCI_LINT_VERSION=v2.10.1
MAGE_X_GORELEASER_VERSION=v2.13.3
MAGE_X_GOVULNCHECK_VERSION=v1.1.4
MAGE_X_GO_SECONDARY_VERSION=1.24.x
Expand Down
4 changes: 2 additions & 2 deletions .github/env/10-pre-commit.env
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
# 🪝 PRE-COMMIT TOOL VERSION
# ================================================================================================

GO_PRE_COMMIT_VERSION=v1.6.1
GO_PRE_COMMIT_VERSION=v1.6.2
GO_PRE_COMMIT_USE_LOCAL=false

# ================================================================================================
Expand All @@ -52,7 +52,7 @@ GO_PRE_COMMIT_ALL_FILES=true
# 🛠️ TOOL VERSIONS
# ================================================================================================

GO_PRE_COMMIT_GOLANGCI_LINT_VERSION=v2.9.0
GO_PRE_COMMIT_GOLANGCI_LINT_VERSION=v2.10.1
GO_PRE_COMMIT_FUMPT_VERSION=v0.9.2
GO_PRE_COMMIT_GOIMPORTS_VERSION=latest
GO_PRE_COMMIT_GITLEAKS_VERSION=v8.30.0
Expand Down
16 changes: 8 additions & 8 deletions .github/workflows/fortress-benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -390,19 +390,19 @@ jobs:
# --------------------------------------------------------------------
- name: 📤 Upload benchmark statistics
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: bench-stats-${{ matrix.os }}-${{ matrix.go-version }}
path: bench-stats-${{ matrix.os }}-${{ matrix.go-version }}.json
retention-days: 7
artifact-name: bench-stats-${{ matrix.os }}-${{ matrix.go-version }}
artifact-path: bench-stats-${{ matrix.os }}-${{ matrix.go-version }}.json
retention-days: "7"

# --------------------------------------------------------------------
# Upload raw benchmark results
# --------------------------------------------------------------------
- name: 📤 Upload benchmark results
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: bench-results-${{ matrix.os }}-${{ matrix.go-version }}
path: bench-results-${{ matrix.os }}-${{ matrix.go-version }}.txt
retention-days: 7 # Keep raw results longer for analysis
artifact-name: bench-results-${{ matrix.os }}-${{ matrix.go-version }}
artifact-path: bench-results-${{ matrix.os }}-${{ matrix.go-version }}.txt
retention-days: "7"
24 changes: 12 additions & 12 deletions .github/workflows/fortress-code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,11 @@ jobs:
# --------------------------------------------------------------------
- name: 📤 Upload go vet results
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: govet-results
path: govet-output.log
retention-days: 7
artifact-name: govet-results
artifact-path: govet-output.log
retention-days: "7"
if-no-files-found: ignore

# --------------------------------------------------------------------
Expand Down Expand Up @@ -513,11 +513,11 @@ jobs:
# --------------------------------------------------------------------
- name: 📤 Upload lint results
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: lint-results
path: lint-output.log
retention-days: 7
artifact-name: lint-results
artifact-path: lint-output.log
retention-days: "7"
if-no-files-found: ignore

# --------------------------------------------------------------------
Expand Down Expand Up @@ -765,11 +765,11 @@ jobs:
# --------------------------------------------------------------------
- name: 📤 Upload format check results
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: format-check-results
path: format-output.log
retention-days: 7
artifact-name: format-check-results
artifact-path: format-output.log
retention-days: "7"
if-no-files-found: ignore

# --------------------------------------------------------------------
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/fortress-completion-statistics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -685,11 +685,11 @@ jobs:
# --------------------------------------------------------------------
- name: 📤 Upload LOC Stats JSON
if: always() && hashFiles('loc-stats.json') != ''
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: loc-stats
path: loc-stats.json
retention-days: 7
artifact-name: loc-stats
artifact-path: loc-stats.json
retention-days: "7"

- name: 📤 Upload Statistics Section
id: upload-section
Expand Down
35 changes: 17 additions & 18 deletions .github/workflows/fortress-coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2367,22 +2367,22 @@ jobs:
# --------------------------------------------------------------------
- name: 📤 Upload performance cache statistics
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: cache-stats-coverage
path: cache-stats-coverage.json
retention-days: 1
artifact-name: cache-stats-coverage
artifact-path: cache-stats-coverage.json
retention-days: "1"

# --------------------------------------------------------------------
# Upload coverage statistics for completion report
# --------------------------------------------------------------------
- name: 📤 Upload coverage statistics
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: coverage-stats-internal
path: coverage-stats-internal-*.json
retention-days: 1
artifact-name: coverage-stats-internal
artifact-path: coverage-stats-internal-*.json
retention-days: "7"

# --------------------------------------------------------------------
# Upload coverage history for future runs (WORKING SYSTEM - PRESERVED)
Expand Down Expand Up @@ -2410,13 +2410,12 @@ jobs:
- name: 📤 Upload coverage history artifacts
# Upload history for all branches to preserve trend data
if: github.event_name == 'push'
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: coverage-history-${{ inputs.commit-sha }}
path: .github/coverage/history/*.json
retention-days: 90
compression-level: 9
continue-on-error: true
artifact-name: coverage-history-${{ inputs.commit-sha }}
artifact-path: .github/coverage/history/*.json
retention-days: "90"
compression-level: "9"
# ----------------------------------------------------------------------------------
# Upload Coverage to Codecov (External Provider)
# ----------------------------------------------------------------------------------
Expand Down Expand Up @@ -2594,8 +2593,8 @@ jobs:

- name: 📤 Upload coverage statistics (Codecov)
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: coverage-stats-codecov
path: coverage-stats-codecov-*.json
retention-days: 7
artifact-name: coverage-stats-codecov
artifact-path: coverage-stats-codecov-*.json
retention-days: "7"
16 changes: 8 additions & 8 deletions .github/workflows/fortress-security-scans.yml
Original file line number Diff line number Diff line change
Expand Up @@ -219,11 +219,11 @@ jobs:
# --------------------------------------------------------------------
- name: 📤 Upload Nancy scan results
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: nancy-scan-results
path: nancy-output.log
retention-days: 7
artifact-name: nancy-scan-results
artifact-path: nancy-output.log
retention-days: "7"
if-no-files-found: ignore

# --------------------------------------------------------------------
Expand Down Expand Up @@ -458,11 +458,11 @@ jobs:
# --------------------------------------------------------------------
- name: 📤 Upload govulncheck results
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: govulncheck-scan-results
path: govulncheck-output.log
retention-days: 7
artifact-name: govulncheck-scan-results
artifact-path: govulncheck-output.log
retention-days: "7"
if-no-files-found: ignore

# --------------------------------------------------------------------
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/fortress-test-fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,11 @@ jobs:
# --------------------------------------------------------------------
- name: 📤 Upload fuzz test outputs
if: always()
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
uses: ./.github/actions/upload-artifact-resilient
with:
name: test-results-fuzz-${{ inputs.primary-runner }}-${{ inputs.go-primary-version }}
path: |
artifact-name: test-results-fuzz-${{ inputs.primary-runner }}-${{ inputs.go-primary-version }}
artifact-path: |
.mage-x/ci-results-fuzz.jsonl
fuzz-output.log
retention-days: 1
retention-days: "7"
if-no-files-found: ignore
Loading