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}`
|
||||
})
|
39
Dockerfile
Normal file
39
Dockerfile
Normal file
@ -0,0 +1,39 @@
|
||||
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 \
|
||||
--activate-profiles docker-dependencies
|
||||
|
||||
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 \
|
||||
--offline \
|
||||
--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
|
10
README.md
10
README.md
@ -39,3 +39,13 @@ can be performed.
|
||||
Go to `Settings -> Language & Frameworks -> JavaScript -> Prettier` and then check
|
||||
`Automatic Prettier Configuration`, set `Run for files` to `**/*.{java}`,
|
||||
and finally check `Run on save`.
|
||||
|
||||
## Test servers
|
||||
All pull requests are automatically deployed to a test server.
|
||||
The URL to the test server will be posted as a comment in the pull request once deployed.
|
||||
|
||||
Prepare test data in the `DataInitializer` class to help others test your changes.
|
||||
Document (in the pull request) which users to log in as and what to do to see the changes.
|
||||
|
||||
If you want to reset the data to its original state you can re-run the "deploy-branch.yaml"
|
||||
workflow at https://gitea.dsv.su.se/DMC/scipro/actions for the branch you want to reset.
|
||||
|
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
|
72
war/pom.xml
72
war/pom.xml
@ -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,53 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>branch</id>
|
||||
<properties>
|
||||
<spring.profile.active>branch</spring.profile.active>
|
||||
</properties>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>docker-dependencies</id>
|
||||
<!--
|
||||
Some dependencies are not discovered by default when running dependency:go-offline.
|
||||
They are added here manually to allow Docker build layers to be cached properly.
|
||||
-->
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml</groupId>
|
||||
<artifactId>classmate</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.bytebuddy</groupId>
|
||||
<artifactId>byte-buddy-agent</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
<version>${querydsl.version}</version>
|
||||
<classifier>jakarta</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.minidev</groupId>
|
||||
<artifactId>json-smart</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
@ -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();
|
||||
|
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-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
|
||||
|
Loading…
x
Reference in New Issue
Block a user