diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 4dd2b3f..116e0ee 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -32,4 +32,4 @@ permissions: jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@7fdc2705df74b4e352d2a1cde3e87a5923fdf329 \ No newline at end of file + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@7fdc2705df74b4e352d2a1cde3e87a5923fdf329 diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index bcea7df..f306483 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -412,4 +412,4 @@ jobs: repo: context.repo.repo, issue_number: context.issue.number, body: comment - }); \ No newline at end of file + }); diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 20a8bd4..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,151 +0,0 @@ -# GitLab CI Configuration for Branch Newspaper -# This pipeline calls shared scripts in ci-scripts/ for consistency with GitHub Actions - -stages: - - lint - - test - - build - - deploy - -variables: - MIX_ENV: test - ELIXIR_VERSION: "1.15.7" - OTP_VERSION: "26.2" - -# Cache dependencies between jobs -.cache_template: &cache_template - cache: - key: - files: - - mix.lock - paths: - - deps/ - - _build/ - -# Base template for Elixir jobs -.elixir_template: &elixir_template - image: elixir:${ELIXIR_VERSION} - before_script: - - mix local.hex --force - - mix local.rebar --force - - mix deps.get - -# Lint job -lint: - <<: *elixir_template - <<: *cache_template - stage: lint - script: - - chmod +x ./ci-scripts/*.sh - - ./ci-scripts/lint.sh - allow_failure: false - -# Test job - Primary version -test: - <<: *elixir_template - <<: *cache_template - stage: test - script: - - chmod +x ./ci-scripts/*.sh - - ./ci-scripts/setup.sh - - ./ci-scripts/test.sh - artifacts: - when: always - reports: - junit: _build/test/lib/branch_newspaper/test-junit-report.xml - expire_in: 1 week - -# Test job - Matrix for additional Elixir versions -test:elixir-1.16: - <<: *elixir_template - <<: *cache_template - stage: test - image: elixir:1.16.0 - script: - - chmod +x ./ci-scripts/*.sh - - ./ci-scripts/setup.sh - - ./ci-scripts/test.sh - allow_failure: true - -test:elixir-1.15-minimum: - <<: *elixir_template - <<: *cache_template - stage: test - image: elixir:1.15.0 - script: - - chmod +x ./ci-scripts/*.sh - - ./ci-scripts/setup.sh - - ./ci-scripts/test.sh - allow_failure: true - -# Build release -build: - <<: *elixir_template - stage: build - variables: - MIX_ENV: prod - cache: - key: - files: - - mix.lock - prefix: prod - paths: - - deps/ - - _build/ - script: - - chmod +x ./ci-scripts/*.sh - - ./ci-scripts/build.sh - artifacts: - paths: - - _build/prod/rel/branch_newspaper/ - expire_in: 1 week - only: - - main - - master - - tags - -# Deploy to staging (manual trigger) -deploy:staging: - stage: deploy - image: alpine:latest - before_script: - - apk add --no-cache openssh-client rsync - script: - - echo "Deploying to staging environment..." - - echo "This is a placeholder - configure actual deployment" - environment: - name: staging - url: https://staging.branch-newspaper.example.com - when: manual - only: - - main - - develop - -# Deploy to production (manual trigger, only on tags) -deploy:production: - stage: deploy - image: alpine:latest - before_script: - - apk add --no-cache openssh-client rsync - script: - - echo "Deploying to production environment..." - - echo "This is a placeholder - configure actual deployment" - environment: - name: production - url: https://branch-newspaper.example.com - when: manual - only: - - tags - -# Mirror verification (runs after pushes from GitHub) -verify-mirror: - stage: test - image: alpine/git:latest - script: - - chmod +x ./ci-scripts/*.sh - - apk add --no-cache bash - - ./ci-scripts/verify-mirror.sh || true - only: - - main - - master - allow_failure: true diff --git a/ci-scripts/build.sh b/ci-scripts/build.sh deleted file mode 100755 index 08b88c9..0000000 --- a/ci-scripts/build.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -# CI Build Script - Shared between GitHub Actions and GitLab CI -# This script builds the application for deployment - -set -euo pipefail - -echo "==> Building Branch Newspaper" - -# Set environment -export MIX_ENV="${MIX_ENV:-prod}" - -echo "==> Building in $MIX_ENV environment" - -# Get dependencies -echo "==> Getting dependencies..." -mix deps.get --only $MIX_ENV - -# Compile -echo "==> Compiling..." -mix compile - -# Build assets -echo "==> Building assets..." -if [ -f "assets/package.json" ]; then - cd assets - npm ci - cd .. -fi - -# Deploy assets (esbuild + tailwind) -mix assets.deploy - -# Create release -if grep -q "releases:" mix.exs 2>/dev/null; then - echo "==> Creating release..." - mix release --overwrite - - # Show release info - echo "" - echo "==> Release created successfully!" - ls -la _build/$MIX_ENV/rel/branch_newspaper/ 2>/dev/null || true -else - echo "==> Skipping release (not configured in mix.exs)" -fi - -echo "==> Build complete!" diff --git a/ci-scripts/lint.sh b/ci-scripts/lint.sh deleted file mode 100755 index d356559..0000000 --- a/ci-scripts/lint.sh +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bash -# CI Lint Script - Shared between GitHub Actions and GitLab CI -# This script runs code quality checks - -set -euo pipefail - -echo "==> Running Branch Newspaper Linting & Code Quality Checks" - -EXIT_CODE=0 - -# Check formatting -echo "==> Checking code formatting..." -if ! mix format --check-formatted; then - echo "ERROR: Code is not properly formatted. Run 'mix format' to fix." - EXIT_CODE=1 -fi - -# Check for unused dependencies -echo "==> Checking for unused dependencies..." -if mix deps.unlock --check-unused 2>/dev/null; then - echo "OK: No unused dependencies found." -else - echo "WARNING: Unused dependencies detected." - # Don't fail on unused deps, just warn -fi - -# Compile with warnings as errors -echo "==> Compiling with warnings as errors..." -if ! mix compile --warnings-as-errors --force; then - echo "ERROR: Compilation warnings found." - EXIT_CODE=1 -fi - -# Run Credo for static code analysis (if available) -if grep -q "credo" mix.exs 2>/dev/null; then - echo "==> Running Credo static analysis..." - if ! mix credo --strict; then - echo "WARNING: Credo found issues." - # Make Credo failures advisory for now - fi -fi - -# Run Dialyzer for type checking (if PLT exists or --dialyzer flag passed) -if [ "${RUN_DIALYZER:-false}" = "true" ]; then - echo "==> Running Dialyzer type analysis..." - if grep -q "dialyxir" mix.exs 2>/dev/null; then - mix dialyzer --format github || echo "WARNING: Dialyzer found issues." - else - echo "SKIP: Dialyxir not configured." - fi -fi - -# Check for security vulnerabilities in dependencies -if grep -q "sobelow" mix.exs 2>/dev/null; then - echo "==> Running Sobelow security scan..." - mix sobelow --config || echo "WARNING: Sobelow found potential issues." -fi - -# Check for outdated dependencies -echo "==> Checking for outdated dependencies..." -mix hex.outdated || true - -echo "==> Lint checks complete with exit code: $EXIT_CODE" -exit $EXIT_CODE diff --git a/ci-scripts/mirror-push.sh b/ci-scripts/mirror-push.sh deleted file mode 100755 index c97edc1..0000000 --- a/ci-scripts/mirror-push.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -# Mirror Push Script - Pushes changes from GitHub to GitLab -# This is triggered by GitHub Actions on push events (event-driven, not polling) - -set -euo pipefail - -echo "==> Pushing to GitLab Mirror" - -# Configuration from environment -GITLAB_URL="${GITLAB_URL:-git@gitlab.com:maa-framework/3-applications/branch-newspaper.git}" -GITLAB_REMOTE_NAME="gitlab-mirror" - -# Setup GitLab remote if not exists -if ! git remote get-url "$GITLAB_REMOTE_NAME" &>/dev/null; then - echo "==> Adding GitLab mirror remote..." - git remote add "$GITLAB_REMOTE_NAME" "$GITLAB_URL" -fi - -# Get current branch/ref -CURRENT_REF="${GITHUB_REF:-$(git symbolic-ref HEAD 2>/dev/null || git rev-parse HEAD)}" -CURRENT_BRANCH="${CURRENT_REF#refs/heads/}" - -echo "==> Current ref: $CURRENT_REF" -echo "==> Current branch: $CURRENT_BRANCH" - -# Push with retry logic -MAX_RETRIES=4 -RETRY_DELAY=2 - -push_with_retry() { - local target=$1 - local retries=0 - - while [ $retries -lt $MAX_RETRIES ]; do - echo "==> Pushing $target to GitLab (attempt $((retries + 1))/$MAX_RETRIES)..." - - if git push "$GITLAB_REMOTE_NAME" "$target" --force-with-lease 2>&1; then - echo "==> Successfully pushed $target" - return 0 - fi - - retries=$((retries + 1)) - if [ $retries -lt $MAX_RETRIES ]; then - echo "==> Push failed, retrying in ${RETRY_DELAY}s..." - sleep $RETRY_DELAY - RETRY_DELAY=$((RETRY_DELAY * 2)) - fi - done - - echo "==> Failed to push $target after $MAX_RETRIES attempts" - return 1 -} - -# Push branches -echo "==> Pushing branches..." -push_with_retry "$CURRENT_BRANCH" - -# Push tags if this is a tag push -if [[ "$CURRENT_REF" == refs/tags/* ]]; then - TAG_NAME="${CURRENT_REF#refs/tags/}" - echo "==> Pushing tag: $TAG_NAME" - push_with_retry "refs/tags/$TAG_NAME" -fi - -# Optionally push all tags -if [ "${PUSH_ALL_TAGS:-false}" = "true" ]; then - echo "==> Pushing all tags..." - git push "$GITLAB_REMOTE_NAME" --tags --force-with-lease || true -fi - -echo "==> Mirror push complete!" diff --git a/ci-scripts/setup.sh b/ci-scripts/setup.sh deleted file mode 100755 index 1a9ca85..0000000 --- a/ci-scripts/setup.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env bash -# CI Setup Script - Shared between GitHub Actions and GitLab CI -# This script installs dependencies and prepares the environment - -set -euo pipefail - -echo "==> Setting up Branch Newspaper CI environment" - -# Detect OS and architecture -OS=$(uname -s | tr '[:upper:]' '[:lower:]') -ARCH=$(uname -m) -case $ARCH in - x86_64) ARCH="amd64" ;; - aarch64|arm64) ARCH="arm64" ;; -esac - -echo "==> Detected OS: $OS, Architecture: $ARCH" - -# Install Elixir dependencies -echo "==> Installing Mix dependencies..." -mix local.hex --force -mix local.rebar --force -mix deps.get - -# Compile in the appropriate environment -MIX_ENV="${MIX_ENV:-test}" -echo "==> Compiling in $MIX_ENV environment..." -mix compile --warnings-as-errors - -# Setup database -echo "==> Setting up database..." -mix ecto.create || true -mix ecto.migrate - -# Install Node.js dependencies for assets (if needed) -if [ -f "assets/package.json" ]; then - echo "==> Installing Node.js dependencies..." - cd assets && npm ci && cd .. -fi - -# Setup Kubo/IPFS if needed for tests -if [ -d "kubo" ] && [ "${SETUP_IPFS:-false}" = "true" ]; then - echo "==> Setting up IPFS (Kubo)..." - if [ ! -f "kubo/ipfs" ]; then - KUBO_VERSION="${KUBO_VERSION:-v0.24.0}" - KUBO_TARBALL="kubo_${KUBO_VERSION}_${OS}-${ARCH}.tar.gz" - - if [ -f "$KUBO_TARBALL" ]; then - tar -xzf "$KUBO_TARBALL" -C kubo --strip-components=1 - else - echo "Warning: Kubo tarball not found, skipping IPFS setup" - fi - fi -fi - -echo "==> Setup complete!" diff --git a/ci-scripts/test.sh b/ci-scripts/test.sh deleted file mode 100755 index 069c643..0000000 --- a/ci-scripts/test.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -# CI Test Script - Shared between GitHub Actions and GitLab CI -# This script runs all tests and generates coverage reports - -set -euo pipefail - -echo "==> Running Branch Newspaper Tests" - -# Set environment -export MIX_ENV=test - -# Parse arguments -COVERAGE="${COVERAGE:-false}" -FORMAT="${FORMAT:-default}" - -while [[ $# -gt 0 ]]; do - case $1 in - --coverage) - COVERAGE=true - shift - ;; - --format) - FORMAT="$2" - shift 2 - ;; - *) - shift - ;; - esac -done - -# Ensure dependencies are compiled -echo "==> Ensuring test dependencies are compiled..." -mix deps.compile - -# Run database migrations -echo "==> Running database migrations..." -mix ecto.create --quiet || true -mix ecto.migrate --quiet - -# Run tests -echo "==> Running ExUnit tests..." - -TEST_OPTS="" - -if [ "$COVERAGE" = "true" ]; then - echo "==> Coverage reporting enabled" - # If using excoveralls - if grep -q "excoveralls" mix.exs 2>/dev/null; then - case $FORMAT in - github) - mix coveralls.github - ;; - html) - mix coveralls.html - ;; - *) - mix coveralls - ;; - esac - else - mix test --cover - fi -else - mix test $TEST_OPTS -fi - -TEST_EXIT_CODE=$? - -echo "==> Test run complete with exit code: $TEST_EXIT_CODE" -exit $TEST_EXIT_CODE diff --git a/ci-scripts/verify-mirror.sh b/ci-scripts/verify-mirror.sh deleted file mode 100755 index fad605f..0000000 --- a/ci-scripts/verify-mirror.sh +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env bash -# Mirror Verification Script -# Confirms that all branches and tags have been transferred between repositories - -set -euo pipefail - -echo "==> Branch Newspaper Mirror Verification" - -# Configuration -SOURCE_REMOTE="${SOURCE_REMOTE:-gitlab}" -DEST_REMOTE="${DEST_REMOTE:-origin}" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -ERRORS=0 -WARNINGS=0 - -# Function to print status -print_status() { - local status=$1 - local message=$2 - case $status in - ok) - echo -e "${GREEN}[OK]${NC} $message" - ;; - error) - echo -e "${RED}[ERROR]${NC} $message" - ((ERRORS++)) - ;; - warn) - echo -e "${YELLOW}[WARN]${NC} $message" - ((WARNINGS++)) - ;; - esac -} - -# Fetch latest from both remotes -echo "==> Fetching from remotes..." -git fetch "$SOURCE_REMOTE" --tags --prune 2>/dev/null || print_status warn "Could not fetch from $SOURCE_REMOTE" -git fetch "$DEST_REMOTE" --tags --prune 2>/dev/null || print_status warn "Could not fetch from $DEST_REMOTE" - -# Verify branches -echo "" -echo "==> Verifying branches..." - -SOURCE_BRANCHES=$(git branch -r | grep "^ $SOURCE_REMOTE/" | sed "s| $SOURCE_REMOTE/||" | grep -v "HEAD" || true) -DEST_BRANCHES=$(git branch -r | grep "^ $DEST_REMOTE/" | sed "s| $DEST_REMOTE/||" | grep -v "HEAD" || true) - -if [ -z "$SOURCE_BRANCHES" ]; then - print_status warn "No branches found on $SOURCE_REMOTE" -else - for branch in $SOURCE_BRANCHES; do - if echo "$DEST_BRANCHES" | grep -q "^$branch$"; then - # Check if commits match - SOURCE_COMMIT=$(git rev-parse "$SOURCE_REMOTE/$branch" 2>/dev/null || echo "") - DEST_COMMIT=$(git rev-parse "$DEST_REMOTE/$branch" 2>/dev/null || echo "") - - if [ "$SOURCE_COMMIT" = "$DEST_COMMIT" ]; then - print_status ok "Branch '$branch' synced (${SOURCE_COMMIT:0:7})" - else - print_status error "Branch '$branch' out of sync: $SOURCE_REMOTE=${SOURCE_COMMIT:0:7} vs $DEST_REMOTE=${DEST_COMMIT:0:7}" - fi - else - print_status error "Branch '$branch' missing on $DEST_REMOTE" - fi - done -fi - -# Verify tags -echo "" -echo "==> Verifying tags..." - -SOURCE_TAGS=$(git tag -l | sort) -DEST_TAGS=$(git ls-remote --tags "$DEST_REMOTE" 2>/dev/null | awk '{print $2}' | sed 's|refs/tags/||' | grep -v '\^{}' | sort || true) - -if [ -z "$SOURCE_TAGS" ]; then - print_status warn "No tags found in repository" -else - for tag in $SOURCE_TAGS; do - if echo "$DEST_TAGS" | grep -q "^$tag$"; then - print_status ok "Tag '$tag' present on $DEST_REMOTE" - else - print_status error "Tag '$tag' missing on $DEST_REMOTE" - fi - done -fi - -# Verify commit history integrity -echo "" -echo "==> Verifying commit history..." - -# Get the main/master branch -MAIN_BRANCH="" -for branch in main master; do - if git rev-parse --verify "$DEST_REMOTE/$branch" &>/dev/null; then - MAIN_BRANCH=$branch - break - fi -done - -if [ -n "$MAIN_BRANCH" ]; then - COMMIT_COUNT=$(git rev-list --count "$DEST_REMOTE/$MAIN_BRANCH" 2>/dev/null || echo "0") - print_status ok "Commit history intact: $COMMIT_COUNT commits on $MAIN_BRANCH" - - # Show recent commits - echo "" - echo "==> Recent commits on $MAIN_BRANCH:" - git log --oneline -5 "$DEST_REMOTE/$MAIN_BRANCH" 2>/dev/null || true -else - print_status warn "Could not find main/master branch" -fi - -# Summary -echo "" -echo "==> Verification Summary" -echo "========================" -echo "Errors: $ERRORS" -echo "Warnings: $WARNINGS" - -if [ $ERRORS -gt 0 ]; then - echo "" - echo -e "${RED}Mirror verification FAILED${NC}" - exit 1 -else - echo "" - echo -e "${GREEN}Mirror verification PASSED${NC}" - exit 0 -fi