Automate deployment of pull requests (#15)
Click link and see that system is working. Log in using the principal `admin@example.com`. Change something in the deployed system. Re-run the action. See that the database has reset. **Major change** Added OAuth 2 login so no longer need modified web.xml with filter. Run `docker compose up` to start the local OAuth 2 authorization server to log in. Use the custom ticket form and enter the username you want to log in as in the "Principal" field. Squashed all migrations since there are faulty ones that can't be applied to an empty database. Reviewed-on: #15 Reviewed-by: Tom Zhao <tom.zhao@dsv.su.se> Co-authored-by: Andreas Svanberg <andreass@dsv.su.se> Co-committed-by: Andreas Svanberg <andreass@dsv.su.se>
This commit is contained in:
parent
25117c8187
commit
323d6fc61e
.gitea/workflows
DockerfileREADME.mdcompose-branch-deploy.yamlwar
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