Automate deployment of pull requests #15
14
.gitea/workflows/deploy-branch-cleanup.yaml
Normal file
14
.gitea/workflows/deploy-branch-cleanup.yaml
Normal 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'
|
26
.gitea/workflows/deploy-branch.yaml
Normal file
26
.gitea/workflows/deploy-branch.yaml
Normal 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
35
Dockerfile
Normal 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
|
72
compose-branch-deploy.yaml
Normal file
72
compose-branch-deploy.yaml
Normal 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
|
31
war/pom.xml
31
war/pom.xml
@ -12,6 +12,10 @@
|
|||||||
<artifactId>war</artifactId>
|
<artifactId>war</artifactId>
|
||||||
<packaging>war</packaging>
|
<packaging>war</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<spring.profile.active>tomcat</spring.profile.active>
|
||||||
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
@ -88,7 +92,26 @@
|
|||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
<plugins>
|
<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>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
@ -110,4 +133,12 @@
|
|||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>branch</id>
|
||||||
|
<properties>
|
||||||
|
<spring.profile.active>branch</spring.profile.active>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
</project>
|
</project>
|
||||||
|
@ -16,12 +16,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
|
|||||||
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
import org.springframework.boot.autoconfigure.domain.EntityScan;
|
||||||
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer;
|
import org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilderCustomizer;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||||
import org.springframework.orm.jpa.SharedEntityManagerCreator;
|
import org.springframework.orm.jpa.SharedEntityManagerCreator;
|
||||||
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
|
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
|
||||||
|
import org.springframework.web.filter.ForwardedHeaderFilter;
|
||||||
import se.su.dsv.scipro.CoreConfig;
|
import se.su.dsv.scipro.CoreConfig;
|
||||||
import se.su.dsv.scipro.FileSystemStore;
|
import se.su.dsv.scipro.FileSystemStore;
|
||||||
import se.su.dsv.scipro.RepositoryConfiguration;
|
import se.su.dsv.scipro.RepositoryConfiguration;
|
||||||
@ -84,6 +87,22 @@ public class Main extends SpringBootServletInitializer implements ServletContain
|
|||||||
return currentProfile;
|
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
|
@Bean
|
||||||
public FileStore fileStore() {
|
public FileStore fileStore() {
|
||||||
return new FileSystemStore();
|
return new FileSystemStore();
|
||||||
|
19
war/src/main/resources/application-branch.properties
Normal file
19
war/src/main/resources/application-branch.properties
Normal 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=
|
1
war/src/main/resources/application-tomcat.properties
Normal file
1
war/src/main/resources/application-tomcat.properties
Normal file
@ -0,0 +1 @@
|
|||||||
|
spring.datasource.jndi-name=java:/comp/env/jdbc/sciproDS
|
@ -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-version=2
|
||||||
spring.flyway.baseline-on-migrate=true
|
spring.flyway.baseline-on-migrate=true
|
||||||
|
|
||||||
@ -16,17 +16,17 @@ springdoc.swagger-ui.path=/swagger
|
|||||||
springdoc.swagger-ui.persist-authorization=true
|
springdoc.swagger-ui.persist-authorization=true
|
||||||
|
|
||||||
# These will be overwritten by configuration in the environment of servers it is deployed to
|
# 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-id=${OAUTH2_RESOURCE_SERVER_ID:scipro-api-client}
|
||||||
spring.security.oauth2.resourceserver.opaquetoken.client-secret=scipro-api-secret
|
spring.security.oauth2.resourceserver.opaquetoken.client-secret=${OAUTH2_RESOURCE_SERVER_SECRET:scipro-api-secret}
|
||||||
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=http://localhost:59733/introspect
|
spring.security.oauth2.resourceserver.opaquetoken.introspection-uri=${OAUTH2_RESOURCE_SERVER_INTROSPECTION_URI:http://localhost:59733/introspect}
|
||||||
|
|
||||||
# Log in via local OAuth 2 authorization server
|
# 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.user-name-attribute=sub
|
||||||
spring.security.oauth2.client.provider.docker.token-uri=http://localhost:59734/exchange
|
spring.security.oauth2.client.provider.docker.token-uri=${OAUTH2_TOKEN_URI:http://localhost:59734/exchange}
|
||||||
spring.security.oauth2.client.provider.docker.authorization-uri=http://localhost:59734/authorize
|
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.redirect-uri={baseUrl}/login/oauth2/code/{registrationId}
|
||||||
spring.security.oauth2.client.registration.scipro.provider=docker
|
spring.security.oauth2.client.registration.scipro.provider=docker
|
||||||
spring.security.oauth2.client.registration.scipro.client-id=scipro
|
spring.security.oauth2.client.registration.scipro.client-id=${OAUTH2_CLIENT_ID:scipro}
|
||||||
spring.security.oauth2.client.registration.scipro.client-secret=s3cr3t
|
spring.security.oauth2.client.registration.scipro.client-secret=${OAUTH2_CLIENT_SECRET:s3cr3t}
|
||||||
spring.security.oauth2.client.registration.scipro.authorization-grant-type=authorization_code
|
spring.security.oauth2.client.registration.scipro.authorization-grant-type=authorization_code
|
||||||
|
Loading…
x
Reference in New Issue
Block a user