Skip to content

Commit ffdd561

Browse files
Migrate repository to GitHub with CI/CD setup (#2)
…migration This commit establishes the full CI/CD setup for the Branch Newspaper repository migration from GitLab to GitHub, with bi-directional mirroring support. ## CI/CD Infrastructure - Add GitHub Actions CI workflow with Elixir test matrix (1.15.0-1.16.0) - Add mirror-sync workflow for event-driven GitHub → GitLab mirroring - Update .gitlab-ci.yml to use shared scripts for consistency - Configure Dependabot for Elixir, npm, and GitHub Actions updates ## Shared CI Scripts (ci-scripts/) - setup.sh: Environment setup and dependency installation - test.sh: Test execution with coverage support - lint.sh: Code quality checks (format, compile warnings, credo) - build.sh: Release building for deployment - verify-mirror.sh: Mirror synchronization verification - mirror-push.sh: GitLab push with retry logic ## Documentation - README.md: Comprehensive setup guide with badges and project structure - SECRETS.md: Complete secrets configuration guide - TODO.md: Prioritized task backlog from codebase analysis - ROADMAP.adoc: MVP v1.0 deployment roadmap with architecture ## Key Features - Test matrix across Elixir 1.15.0, 1.15.7, 1.16.0 - Parallel test execution - Dependency caching for faster CI runs - Automatic mirror sync on push events - CodeQL security scanning (existing) - Branch protection recommendations documented Co-authored-by: Claude <noreply@anthropic.com>
1 parent eefb18f commit ffdd561

14 files changed

Lines changed: 1880 additions & 7 deletions

File tree

.github/dependabot.yml

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,64 @@
1-
# To get started with Dependabot version updates, you'll need to specify which
2-
# package ecosystems to update and where the package manifests are located.
3-
# Please see the documentation for all configuration options:
1+
# Dependabot configuration for Branch Newspaper
42
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
53

64
version: 2
75
updates:
8-
- package-ecosystem: "" # See documentation for possible values
9-
directory: "/" # Location of package manifests
6+
# Elixir/Mix dependencies
7+
- package-ecosystem: "mix"
8+
directory: "/"
109
schedule:
11-
interval: "daily"
10+
interval: "weekly"
11+
day: "monday"
12+
open-pull-requests-limit: 10
13+
labels:
14+
- "dependencies"
15+
- "elixir"
16+
commit-message:
17+
prefix: "deps(mix):"
18+
groups:
19+
phoenix:
20+
patterns:
21+
- "phoenix*"
22+
- "plug*"
23+
ecto:
24+
patterns:
25+
- "ecto*"
26+
dev-deps:
27+
patterns:
28+
- "credo"
29+
- "dialyxir"
30+
- "sobelow"
31+
- "ex_doc"
32+
update-types:
33+
- "minor"
34+
- "patch"
35+
36+
# GitHub Actions
37+
- package-ecosystem: "github-actions"
38+
directory: "/"
39+
schedule:
40+
interval: "weekly"
41+
day: "monday"
42+
open-pull-requests-limit: 5
43+
labels:
44+
- "dependencies"
45+
- "github-actions"
46+
commit-message:
47+
prefix: "ci(actions):"
48+
49+
# npm dependencies (if assets use npm)
50+
- package-ecosystem: "npm"
51+
directory: "/assets"
52+
schedule:
53+
interval: "weekly"
54+
day: "monday"
55+
open-pull-requests-limit: 5
56+
labels:
57+
- "dependencies"
58+
- "javascript"
59+
commit-message:
60+
prefix: "deps(npm):"
61+
ignore:
62+
# Tailwind and esbuild are managed by Mix
63+
- dependency-name: "tailwindcss"
64+
- dependency-name: "esbuild"

.github/workflows/ci.yml

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, master, develop]
6+
tags: ['v*']
7+
pull_request:
8+
branches: [main, master]
9+
workflow_dispatch:
10+
11+
env:
12+
MIX_ENV: test
13+
ELIXIR_VERSION: '1.15.7'
14+
OTP_VERSION: '26.2'
15+
16+
jobs:
17+
lint:
18+
name: Lint & Format
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
24+
- name: Setup Elixir
25+
uses: erlef/setup-beam@v1
26+
with:
27+
elixir-version: ${{ env.ELIXIR_VERSION }}
28+
otp-version: ${{ env.OTP_VERSION }}
29+
30+
- name: Cache deps
31+
uses: actions/cache@v4
32+
with:
33+
path: |
34+
deps
35+
_build
36+
key: ${{ runner.os }}-mix-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ hashFiles('**/mix.lock') }}
37+
restore-keys: |
38+
${{ runner.os }}-mix-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-
39+
40+
- name: Install dependencies
41+
run: |
42+
mix local.hex --force
43+
mix local.rebar --force
44+
mix deps.get
45+
46+
- name: Run lint checks
47+
run: ./ci-scripts/lint.sh
48+
49+
test:
50+
name: Test (Elixir ${{ matrix.elixir }} / OTP ${{ matrix.otp }})
51+
runs-on: ubuntu-latest
52+
strategy:
53+
fail-fast: false
54+
matrix:
55+
include:
56+
# Primary supported version
57+
- elixir: '1.15.7'
58+
otp: '26.2'
59+
# Latest versions
60+
- elixir: '1.16.0'
61+
otp: '26.2'
62+
# Minimum supported version
63+
- elixir: '1.15.0'
64+
otp: '25.3'
65+
66+
steps:
67+
- name: Checkout code
68+
uses: actions/checkout@v4
69+
70+
- name: Setup Elixir
71+
uses: erlef/setup-beam@v1
72+
with:
73+
elixir-version: ${{ matrix.elixir }}
74+
otp-version: ${{ matrix.otp }}
75+
76+
- name: Cache deps
77+
uses: actions/cache@v4
78+
with:
79+
path: |
80+
deps
81+
_build
82+
key: ${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-${{ hashFiles('**/mix.lock') }}
83+
restore-keys: |
84+
${{ runner.os }}-mix-${{ matrix.elixir }}-${{ matrix.otp }}-
85+
86+
- name: Install dependencies
87+
run: |
88+
mix local.hex --force
89+
mix local.rebar --force
90+
mix deps.get
91+
92+
- name: Setup test environment
93+
run: ./ci-scripts/setup.sh
94+
95+
- name: Run tests
96+
run: ./ci-scripts/test.sh
97+
98+
build:
99+
name: Build Release
100+
runs-on: ubuntu-latest
101+
needs: [lint, test]
102+
if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')
103+
env:
104+
MIX_ENV: prod
105+
106+
steps:
107+
- name: Checkout code
108+
uses: actions/checkout@v4
109+
110+
- name: Setup Elixir
111+
uses: erlef/setup-beam@v1
112+
with:
113+
elixir-version: ${{ env.ELIXIR_VERSION }}
114+
otp-version: ${{ env.OTP_VERSION }}
115+
116+
- name: Cache deps
117+
uses: actions/cache@v4
118+
with:
119+
path: |
120+
deps
121+
_build
122+
key: ${{ runner.os }}-mix-prod-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-${{ hashFiles('**/mix.lock') }}
123+
restore-keys: |
124+
${{ runner.os }}-mix-prod-${{ env.ELIXIR_VERSION }}-${{ env.OTP_VERSION }}-
125+
126+
- name: Build release
127+
run: ./ci-scripts/build.sh
128+
129+
- name: Upload release artifact
130+
uses: actions/upload-artifact@v4
131+
with:
132+
name: branch-newspaper-release
133+
path: _build/prod/rel/branch_newspaper/
134+
if-no-files-found: warn
135+
136+
mirror-to-gitlab:
137+
name: Mirror to GitLab
138+
runs-on: ubuntu-latest
139+
needs: [test]
140+
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/'))
141+
142+
steps:
143+
- name: Checkout code
144+
uses: actions/checkout@v4
145+
with:
146+
fetch-depth: 0
147+
148+
- name: Setup SSH for GitLab
149+
env:
150+
GITLAB_SSH_KEY: ${{ secrets.GITLAB_SSH_KEY }}
151+
run: |
152+
mkdir -p ~/.ssh
153+
echo "$GITLAB_SSH_KEY" > ~/.ssh/gitlab_key
154+
chmod 600 ~/.ssh/gitlab_key
155+
ssh-keyscan gitlab.com >> ~/.ssh/known_hosts
156+
cat >> ~/.ssh/config << EOF
157+
Host gitlab.com
158+
HostName gitlab.com
159+
User git
160+
IdentityFile ~/.ssh/gitlab_key
161+
StrictHostKeyChecking no
162+
EOF
163+
164+
- name: Push to GitLab
165+
env:
166+
GITLAB_URL: git@gitlab.com:maa-framework/3-applications/branch-newspaper.git
167+
run: ./ci-scripts/mirror-push.sh

.github/workflows/mirror-sync.yml

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
name: Mirror Sync
2+
3+
on:
4+
push:
5+
branches: ['**']
6+
tags: ['**']
7+
delete:
8+
# Trigger on branch/tag deletion to sync deletions
9+
workflow_dispatch:
10+
inputs:
11+
full_sync:
12+
description: 'Perform full mirror sync (all branches and tags)'
13+
required: false
14+
default: 'false'
15+
type: boolean
16+
17+
jobs:
18+
mirror-push:
19+
name: Push to GitLab Mirror
20+
runs-on: ubuntu-latest
21+
# Only run on the main repo, not forks
22+
if: github.repository == 'hyperpolymath/branch-newspaper'
23+
24+
steps:
25+
- name: Checkout code
26+
uses: actions/checkout@v4
27+
with:
28+
fetch-depth: 0
29+
30+
- name: Setup SSH for GitLab
31+
env:
32+
GITLAB_SSH_KEY: ${{ secrets.GITLAB_SSH_KEY }}
33+
run: |
34+
if [ -z "$GITLAB_SSH_KEY" ]; then
35+
echo "ERROR: GITLAB_SSH_KEY secret is not set"
36+
echo "Please add a deploy key to GitLab and store the private key as a GitHub secret"
37+
exit 1
38+
fi
39+
40+
mkdir -p ~/.ssh
41+
echo "$GITLAB_SSH_KEY" > ~/.ssh/gitlab_key
42+
chmod 600 ~/.ssh/gitlab_key
43+
ssh-keyscan gitlab.com >> ~/.ssh/known_hosts
44+
45+
cat >> ~/.ssh/config << EOF
46+
Host gitlab.com
47+
HostName gitlab.com
48+
User git
49+
IdentityFile ~/.ssh/gitlab_key
50+
StrictHostKeyChecking no
51+
EOF
52+
53+
- name: Configure Git
54+
run: |
55+
git config --global user.email "github-actions@github.com"
56+
git config --global user.name "GitHub Actions"
57+
58+
- name: Add GitLab remote
59+
run: |
60+
git remote add gitlab git@gitlab.com:maa-framework/3-applications/branch-newspaper.git || true
61+
git remote set-url gitlab git@gitlab.com:maa-framework/3-applications/branch-newspaper.git
62+
63+
- name: Push current ref to GitLab
64+
if: github.event_name == 'push'
65+
run: |
66+
REF="${GITHUB_REF}"
67+
echo "Pushing ref: $REF"
68+
69+
# Retry logic with exponential backoff
70+
MAX_RETRIES=4
71+
RETRY_DELAY=2
72+
73+
for i in $(seq 1 $MAX_RETRIES); do
74+
if git push gitlab "$REF" --force-with-lease; then
75+
echo "Successfully pushed $REF"
76+
exit 0
77+
fi
78+
if [ $i -lt $MAX_RETRIES ]; then
79+
echo "Push failed, retrying in ${RETRY_DELAY}s..."
80+
sleep $RETRY_DELAY
81+
RETRY_DELAY=$((RETRY_DELAY * 2))
82+
fi
83+
done
84+
85+
echo "Failed to push after $MAX_RETRIES attempts"
86+
exit 1
87+
88+
- name: Full mirror sync
89+
if: github.event.inputs.full_sync == 'true' || github.event_name == 'workflow_dispatch'
90+
run: |
91+
echo "Performing full mirror sync..."
92+
93+
# Push all branches
94+
git push gitlab --all --force-with-lease
95+
96+
# Push all tags
97+
git push gitlab --tags --force
98+
99+
echo "Full sync complete!"
100+
101+
- name: Handle deletion
102+
if: github.event_name == 'delete'
103+
run: |
104+
REF_TYPE="${{ github.event.ref_type }}"
105+
REF_NAME="${{ github.event.ref }}"
106+
107+
echo "Handling deletion of $REF_TYPE: $REF_NAME"
108+
109+
if [ "$REF_TYPE" = "branch" ]; then
110+
git push gitlab --delete "$REF_NAME" || echo "Branch may already be deleted"
111+
elif [ "$REF_TYPE" = "tag" ]; then
112+
git push gitlab --delete "refs/tags/$REF_NAME" || echo "Tag may already be deleted"
113+
fi
114+
115+
verify-mirror:
116+
name: Verify Mirror Sync
117+
runs-on: ubuntu-latest
118+
needs: mirror-push
119+
if: always() && needs.mirror-push.result == 'success'
120+
121+
steps:
122+
- name: Checkout code
123+
uses: actions/checkout@v4
124+
with:
125+
fetch-depth: 0
126+
127+
- name: Setup SSH for GitLab
128+
env:
129+
GITLAB_SSH_KEY: ${{ secrets.GITLAB_SSH_KEY }}
130+
run: |
131+
mkdir -p ~/.ssh
132+
echo "$GITLAB_SSH_KEY" > ~/.ssh/gitlab_key
133+
chmod 600 ~/.ssh/gitlab_key
134+
ssh-keyscan gitlab.com >> ~/.ssh/known_hosts
135+
136+
cat >> ~/.ssh/config << EOF
137+
Host gitlab.com
138+
HostName gitlab.com
139+
User git
140+
IdentityFile ~/.ssh/gitlab_key
141+
StrictHostKeyChecking no
142+
EOF
143+
144+
- name: Add GitLab remote
145+
run: |
146+
git remote add gitlab git@gitlab.com:maa-framework/3-applications/branch-newspaper.git || true
147+
148+
- name: Verify mirror sync
149+
env:
150+
SOURCE_REMOTE: origin
151+
DEST_REMOTE: gitlab
152+
run: ./ci-scripts/verify-mirror.sh

0 commit comments

Comments
 (0)