Automate deployment of pull requests #15

Merged
tozh4728 merged 3 commits from branch-deploy into develop 2024-12-19 10:44:49 +01:00
9 changed files with 226 additions and 9 deletions
Showing only changes of commit a2e56af3be - Show all commits

View File

@ -0,0 +1,14 @@
name: Remove branch deployment from branch.dsv.su.se
on:
pull_request:
types:
- closed
jobs:
cleanup:
runs-on: ubuntu-latest
steps:
- uses: https://gitea.dsv.su.se/ansv7779/action-branch-deploy@v2
with:
cleanup-ssh-key: ${{ secrets.BRANCH_CLEANUP_KEY }}
compose-file: compose-branch-deploy.yaml
mode: 'cleanup'

View File

@ -0,0 +1,26 @@
name: Deploy to branch.dsv.su.se
on:
- pull_request
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- id: deploy
uses: https://gitea.dsv.su.se/ansv7779/action-branch-deploy@v2
with:
ssh-key: ${{ secrets.BRANCH_DEPLOY_KEY }}
compose-file: compose-branch-deploy.yaml
- name: Post URL to deployment as comment
uses: actions/github-script@v7
if: github.event.action == 'opened'
env:
BRANCH_URL: ${{ steps.deploy.outputs.url }}
with:
script: |
const url = process.env.BRANCH_URL;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `Deployed to ${url}`
})

35
Dockerfile Normal file
View File

@ -0,0 +1,35 @@
FROM debian:bookworm AS build
RUN apt-get update && apt-get install -y openjdk-17-jdk-headless
WORKDIR /app
COPY pom.xml .
COPY .mvn/ .mvn/
COPY mvnw .
COPY api/pom.xml api/pom.xml
COPY core/pom.xml core/pom.xml
COPY view/pom.xml view/pom.xml
COPY war/pom.xml war/pom.xml
COPY daisy-integration/pom.xml daisy-integration/pom.xml
# Download dependencies in a separate layer to allow caching for future builds
RUN ./mvnw dependency:go-offline --batch-mode --define includeScope=compile
COPY api/src/ api/src/
COPY core/src/ core/src/
COPY view/src/ view/src/
COPY war/src/ war/src/
COPY daisy-integration/src/ daisy-integration/src/
RUN ./mvnw package \
--define skipTests \
--activate-profiles branch,DEV \
--define skip.npm \
--define skip.installnodenpm
FROM tomcat:10 AS run
COPY --from=build /app/war/target/*.war /usr/local/tomcat/webapps/ROOT.war
EXPOSE 8080

View File

@ -0,0 +1,72 @@
services:
scipro:
build:
context: .
dockerfile: Dockerfile
restart: unless-stopped
depends_on:
db:
condition: service_healthy
oauth2:
condition: service_started
environment:
- JDBC_DATABASE_URL=jdbc:mariadb://db:3306/scipro
- JDBC_DATABASE_USERNAME=scipro
- JDBC_DATABASE_PASSWORD=scipro
- OAUTH2_AUTHORIZATION_URI=https://oauth2-${VHOST}/authorize
- OAUTH2_TOKEN_URI=https://oauth2-${VHOST}/exchange
- OAUTH2_USER_INFO_URI=https://oauth2-${VHOST}/verify
- OAUTH2_CLIENT_ID=scipro_client
- OAUTH2_CLIENT_SECRET=scipro_secret
- OAUTH2_RESOURCE_SERVER_ID=scipro_api_client
- OAUTH2_RESOURCE_SERVER_SECRET=scipro_api_secret
- OAUTH2_RESOURCE_SERVER_INTROSPECTION_URI=https://oauth2-${VHOST}/introspect
networks:
- traefik
- internal
labels:
- "traefik.enable=true"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`${VHOST}`)"
- "traefik.http.routers.${COMPOSE_PROJECT_NAME}.tls.certresolver=letsencrypt"
db:
image: mariadb:10.11
restart: unless-stopped
networks:
- internal
environment:
MARIADB_ROOT_PASSWORD: root
MARIADB_DATABASE: scipro
MARIADB_USER: scipro
MARIADB_PASSWORD: scipro
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect"]
start_period: 10s
interval: 10s
timeout: 5s
retries: 6
oauth2:
build:
context: https://github.com/dsv-su/toker.git
dockerfile: embedded.Dockerfile
restart: unless-stopped
environment:
- CLIENT_ID=scipro_client
- CLIENT_SECRET=scipro_secret
- CLIENT_REDIRECT_URI=https://${VHOST}/login/oauth2/code/scipro
- RESOURCE_SERVER_ID=scipro_api_client
- RESOURCE_SERVER_SECRET=scipro_api_secret
networks:
- traefik
labels:
- "traefik.enable=true"
- "traefik.http.routers.oauth2-${COMPOSE_PROJECT_NAME}.rule=Host(`oauth2-${VHOST}`)"
- "traefik.http.routers.oauth2-${COMPOSE_PROJECT_NAME}.tls.certresolver=letsencrypt"
networks:
traefik:
name: traefik
external: true
internal:
name: ${COMPOSE_PROJECT_NAME}_internal

View File

@ -12,6 +12,10 @@
<artifactId>war</artifactId>
<packaging>war</packaging>
<properties>
<spring.profile.active>tomcat</spring.profile.active>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
@ -88,7 +92,26 @@
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<propertiesEncoding>${project.build.sourceEncoding}</propertiesEncoding>
<delimiters>
<!-- delimiter for resource filtering is changed since Spring hijacks ${...} -->
<delimiter>@</delimiter>
</delimiters>
<useDefaultDelimiters>false</useDefaultDelimiters>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
@ -110,4 +133,12 @@
</plugins>
</build>
<profiles>
<profile>
<id>branch</id>
<properties>
<spring.profile.active>branch</spring.profile.active>
</properties>
</profile>
</profiles>
</project>

View File

@ -16,12 +16,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.orm.jpa.SharedEntityManagerCreator;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.web.filter.ForwardedHeaderFilter;
import se.su.dsv.scipro.CoreConfig;
import se.su.dsv.scipro.FileSystemStore;
import se.su.dsv.scipro.RepositoryConfiguration;
@ -84,6 +87,22 @@ public class Main extends SpringBootServletInitializer implements ServletContain
return currentProfile;
}
/**
* Spring runs on HTTP and is protected by a HTTPS proxy.
* This filter takes the `X-Forwarded-*` headers and updates the request to reflect the original HTTP request.
* <p>
* Note: This is not needed when we're running behind Apache as a proxy since it uses an AJP connector that has a
* built-in mechanism for handling this,
* see <a href="https://tomcat.apache.org/connectors-doc/common_howto/proxy.html#AJP_as_a_Solution">AJP proxy documentation</a>.
* So this is only for the temporary test servers that are running behind Traefik and use regular HTTP.
*/
@Bean
public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
var filterRegistrationBean = new FilterRegistrationBean<>(new ForwardedHeaderFilter());
filterRegistrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return filterRegistrationBean;
}
@Bean
public FileStore fileStore() {
return new FileSystemStore();

View File

@ -0,0 +1,19 @@
spring.datasource.url=${JDBC_DATABASE_URL}
spring.datasource.username=${JDBC_DATABASE_USERNAME}
spring.datasource.password=${JDBC_DATABASE_PASSWORD}
profile=DEV
# No secrets available for branch deployment to branch.dsv.su.se
# Will have to set up some mock API for this later
service.grading.url=
oauth.uri=
oauth.clientId=
oauth.clientSecret=
oauth.redirectUri=
# No secrets available for branch deployment to branch.dsv.su.se
# Will have to set up some mock API for this later
daisy.api.url=
daisy.api.username=
daisy.api.password=

View File

@ -0,0 +1 @@
spring.datasource.jndi-name=java:/comp/env/jdbc/sciproDS

View File

@ -1,4 +1,4 @@
spring.datasource.jndi-name=java:/comp/env/jdbc/sciproDS
spring.profiles.active=@spring.profile.active@
spring.flyway.baseline-version=2
spring.flyway.baseline-on-migrate=true
@ -16,17 +16,17 @@ springdoc.swagger-ui.path=/swagger
springdoc.swagger-ui.persist-authorization=true
# These will be overwritten by configuration in the environment of servers it is deployed to
spring.security.oauth2.resourceserver.opaquetoken.client-id=scipro-api-client
spring.security.oauth2.resourceserver.opaquetoken.client-secret=scipro-api-secret
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:59733/introspect
spring.security.oauth2.resourceserver.opaquetoken.client-id=${OAUTH2_RESOURCE_SERVER_ID:scipro-api-client}
spring.security.oauth2.resourceserver.opaquetoken.client-secret=${OAUTH2_RESOURCE_SERVER_SECRET:scipro-api-secret}
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=${OAUTH2_RESOURCE_SERVER_INTROSPECTION_URI:http://localhost:59733/introspect}
# Log in via local OAuth 2 authorization server
spring.security.oauth2.client.provider.docker.user-info-uri=http://localhost:59734/verify
spring.security.oauth2.client.provider.docker.user-info-uri=${OAUTH2_USER_INFO_URI:http://localhost:59734/verify}
spring.security.oauth2.client.provider.docker.user-name-attribute=sub
spring.security.oauth2.client.provider.docker.token-uri=http://localhost:59734/exchange
spring.security.oauth2.client.provider.docker.authorization-uri=http://localhost:59734/authorize
spring.security.oauth2.client.provider.docker.token-uri=${OAUTH2_TOKEN_URI:http://localhost:59734/exchange}
spring.security.oauth2.client.provider.docker.authorization-uri=${OAUTH2_AUTHORIZATION_URI:http://localhost:59734/authorize}
spring.security.oauth2.client.registration.scipro.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}
spring.security.oauth2.client.registration.scipro.provider=docker
spring.security.oauth2.client.registration.scipro.client-id=scipro
spring.security.oauth2.client.registration.scipro.client-secret=s3cr3t
spring.security.oauth2.client.registration.scipro.client-id=${OAUTH2_CLIENT_ID:scipro}
spring.security.oauth2.client.registration.scipro.client-secret=${OAUTH2_CLIENT_SECRET:s3cr3t}
spring.security.oauth2.client.registration.scipro.authorization-grant-type=authorization_code