diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
new file mode 100644
index 000000000..d64e15229
--- /dev/null
+++ b/.github/workflows/linters.yml
@@ -0,0 +1,39 @@
+name: Lint
+
+on:
+ push:
+
+jobs:
+ lint:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Ruby
+ uses: ruby/setup-ruby@v1
+ with:
+ ruby-version: 3.2
+ bundler-cache: true
+
+ - name: Lint code for consistent style
+ run: bundle exec rubocop --parallel
+
+ stylelint:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v2
+ with:
+ node-version: '20'
+
+ - name: Install dependencies
+ run: yarn install
+
+ - name: Lint CSS and SCSS files
+ run: npx stylelint "public/stylesheets/**/*.css"
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 000000000..4d5e0e200
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,108 @@
+name: Tests
+
+on:
+ push:
+
+jobs:
+ tests:
+ name: test ${{matrix.db}} ruby-${{ matrix.ruby }}
+ runs-on: ubuntu-latest
+
+ strategy:
+ matrix:
+ ruby: ['3.0', '3.1', '3.2']
+ db: ['postgresql', 'mysql2', 'sqlite3']
+ fail-fast: false
+
+ services:
+ postgres:
+ image: postgres:13
+ env:
+ POSTGRES_DB: redmine_test
+ POSTGRES_USER: root
+ POSTGRES_PASSWORD: root
+ ports:
+ - 5432:5432
+ options: >-
+ --health-cmd pg_isready
+ --health-interval 10s
+ --health-timeout 5s
+ --health-retries 5
+
+ mysql:
+ image: mysql:8.0
+ env:
+ MYSQL_DATABASE: redmine_test
+ MYSQL_ROOT_PASSWORD: 'root'
+ ports:
+ - 3306:3306
+ options: >-
+ --health-cmd="mysqladmin ping"
+ --health-interval=10s
+ --health-timeout=5s
+ --health-retries=3
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Install dependencies and configure environment
+ run: |
+ sudo apt-get update
+ sudo apt-get install --yes --quiet ghostscript gsfonts locales bzr cvs
+ sudo locale-gen en_US # for bazaar non ascii test
+
+ - name: Allow imagemagick to read PDF files
+ run: |
+ echo '' > policy.xml
+ echo '' >> policy.xml
+ echo '' >> policy.xml
+ sudo rm /etc/ImageMagick-6/policy.xml
+ sudo mv policy.xml /etc/ImageMagick-6/policy.xml
+
+ - if: ${{ matrix.db == 'sqlite3' }}
+ name: Prepare test database for sqlite3
+ run: |
+ cat > config/database.yml < config/database.yml <