Workshop 6#
This workshop introduces continuous integration and continuous delivery (CI/CD), and walks you through building a pipeline for your team’s repository with GitHub Actions.
Everything here targets the stack you’re building on: a Java back-end built with Maven, packaged as a Docker image and deployed to Render. The pipeline below works the same whether your build produces a WAR (the setup in the course notes) or an executable JAR — only the artifact filename changes, and that is flagged where it matters.
Specifically, it covers:
why teams automate build, test and deployment steps
the anatomy of a GitHub Actions workflow
a taxonomy of pipeline stages — what a pipeline should actually do
how to build a pipeline that fits inside the subject’s shared Actions minutes budget
where Docker fits between the Maven build and the runtime
how CI results can control whether Render deploys
how to deploy the exact commit represented by a release tag
a hands-on, step-by-step build of that pipeline in your own repository
By the end of this session
Your team repository should have a working CI/CD pipeline that automatically checks code quality, runs tests, and (optionally) deploys your application every time someone pushes code or opens a pull request.
Tip
Your CI/CD pipeline is a living part of your project. Start with the basics today and evolve it as your project grows — add integration tests, security scanning, or automated releases when you’re ready.
Why CI/CD?#
The problem#
Without automation, teams rely on manual processes: “Did you run the tests before merging?” “Is the formatting consistent?” “Does the build still work?” These questions lead to inconsistency, broken main branches, and stressful deployments.
The solution#
Term |
What it means |
Analogy |
|---|---|---|
Continuous Integration (CI) |
Automatically build, lint, and test code on every push or pull request. |
A spell-checker that runs every time you save a document. |
Continuous Delivery (CD) |
Automatically prepare a release-ready artifact after CI passes. |
The document is formatted and ready to print at any moment. |
Continuous Deployment |
Automatically deploy to production when CI/CD passes on the main branch. |
The document is printed and mailed the moment it’s approved. |
There’s a direct assessment argument too. Parts 2 and 3 are assessed from a tagged release that is deployed and running, and you demonstrate your pipeline at the interactive oral. A pipeline that builds and deploys the tag for you turns “did someone remember to redeploy before the deadline?” into a question nobody has to ask.
Anatomy of a GitHub Actions workflow#
Every pipeline lives in a YAML file inside .github/workflows/. Here is the skeleton:
name: CI # Display name in the Actions tab
on: # Trigger events
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs: # One or more jobs (run in parallel by default)
build:
runs-on: ubuntu-latest # The virtual machine type
steps:
- uses: actions/checkout@v4 # Step 1: clone the repo
- name: Run a command # Step 2: any shell command
run: echo "Pipeline is running!"
Key concepts#
Concept |
Explanation |
|---|---|
Workflow |
The entire YAML file. A repo can have multiple workflows. |
Trigger ( |
When the workflow runs: |
Job |
A unit of work that runs on a fresh virtual machine. Jobs run in parallel unless you add |
Step |
A single task inside a job — either a shell command ( |
Action |
A reusable, community-maintained step (e.g., |
Artifact |
A file produced by a job (e.g., a build output, test report) that can be downloaded or passed to another job. |
Every job starts from nothing
A job runs on a fresh virtual machine. It has no JDK configured, no local Maven repository, and no copy of your code until you check it out. This is exactly why CI catches “works on my machine” problems — and, as the next section shows, it’s also why every extra job costs you real time.
Pipeline taxonomy — what should your pipeline do?#
A well-designed pipeline is made up of stages, each with a clear purpose. Below is a taxonomy of common pipeline stages. You do not need all of them on day one — start small and grow.
Code quality
Checkstyle, formatting, static analysis.
Test
JUnit unit tests, integration tests, coverage.
Build
Compile, package the deployable artifact, upload it.
Review gates
Require passing checks on PRs, auto-assign reviewers.
Deploy (CD)
Deploy to Render on merge, or on a release tag.
Stages 1 to 4 typically run on every push and pull request; stage 5 usually runs on the main branch only, or on a tag.
Conveniently, the first three map onto phases of the Maven build lifecycle, so a single mvn verify runs all of them in one pass. Keep that in mind as you read — it’s the key to a cheap pipeline.
Code quality — “is the code clean?”#
Purpose: Enforce consistent style and catch common mistakes before anyone reviews the code.
Tool |
What it does |
Maven goal |
|---|---|---|
Checkstyle |
Style and convention checking against a rule set (Google or Sun checks make a good starting point) |
|
Spotless |
Auto-formatting, with a |
|
PMD |
Static analysis for common bad practice, dead code, and copy-paste |
|
SpotBugs |
Bytecode analysis for likely bugs — null dereferences, unclosed resources, ignored return values |
|
Pick one to start with. Checkstyle is the usual choice because the rules are easy to agree on and the failures are unambiguous. SpotBugs earns its place later in the semester — it’s good at spotting the resource leaks that show up once you’re managing your own JDBC connections.
Bind the check to a phase
If you bind these plugins to a lifecycle phase in your pom.xml (Checkstyle to validate, SpotBugs to verify) they run as part of mvn verify — no separate CI step, and the same command works on your laptop.
Automated testing — “does the code work?”#
Purpose: Run your test suite automatically so broken code never reaches the main branch.
Test type |
When to use |
How it runs |
|---|---|---|
Unit tests |
Test individual classes in isolation — mappers, domain logic, your Unit of Work. Every team should have these. |
JUnit 5 via Surefire, |
Integration tests |
Test components together against a real PostgreSQL database. |
JUnit 5 + Testcontainers via Failsafe, named |
Performance tests |
Measure throughput and latency under load (Part 3). |
k6 — see Workshop 8. Run these on a schedule or manually, not on every push |
Code coverage measures what percentage of your code is exercised by tests. JaCoCo is the standard choice: bind prepare-agent and report in your pom.xml and a report appears in target/site/jacoco/ on every mvn verify.
Testcontainers costs minutes
Testcontainers is excellent — it spins up a real PostgreSQL container so your mappers are tested against the database they’ll actually run on. It also pulls a Docker image on every CI run. Keep integration tests in a separate Failsafe run and consider limiting them to pull requests against main rather than every push to a feature branch.
Build verification — “does the project compile and package?”#
Purpose: Make sure the project can be successfully built from a clean environment — not just on your laptop.
Task |
Command |
Why it matters |
|---|---|---|
Compile and test |
|
Catches missing dependencies, and anything that only compiles because of stale state in your IDE. |
Package the artifact |
mvn package |
Produces the WAR (or executable JAR) that your Dockerfile copies into the image Render runs. If this breaks, you cannot deploy. |
Upload the artifact |
actions/upload-artifact@v4 |
Lets you download the exact artefact a run produced, which is invaluable when the deployed build misbehaves but your local one doesn’t. |
Where Docker fits#
The application has two different packaging steps, and it is useful to keep them conceptually separate:
source code
|
v
Maven
|
+---- compile
|
+---- test
|
+---- package
|
v
WAR / executable JAR
|
v
Docker build
|
v
Docker image
|
v
Render
Maven packages the Java application. Docker packages the application together with the runtime environment and startup configuration. Render then runs the resulting service.
For the standard course setup, the WAR is the Maven artifact that is copied into the Docker image. If a team instead uses an executable JAR, the same idea applies: the JAR is the Maven artifact and the Docker image is the runtime deployment unit.
The industrial intuition
Render is hiding several lower-level deployment operations that you would otherwise have to manage yourself. In a more infrastructure-heavy platform, you would configure a desired runtime state, provide the application artifact or image, and have a deployment system start new instances, perform health checks, replace old instances, and roll out a new version. Render gives you a managed service that performs those operational steps for you.
You do not need to implement Kubernetes for this workshop. The important lesson is to understand the boundary: GitHub Actions decides whether a version is ready; the deployment platform turns that version into running application instances.
A simplified mental model is:
GitHub Actions Render
commit
|
v
verify + package
|
v
artifact / Docker image
|
+------ "deploy this version" ------> deployment service
|
+-- build/pull image
+-- start instances
+-- run health checks
+-- replace old version
|
v
live app
This is why a line such as curl "$DEPLOY_HOOK" can look deceptively small. The HTTP request is only the trigger; the deployment work happens inside the platform. Render provides deploy hooks for this purpose and records the resulting deployment in the service’s deployment history. See the Render deployment documentation and Deploy Hooks.
Review gates — “is this PR safe to merge?”#
Purpose: Use GitHub features to prevent merging code that hasn’t passed your pipeline.
Feature |
How to set it up |
|---|---|
Required status checks |
Settings → Branches → Branch protection rules → Require status checks to pass. Select your CI job names. |
Required reviews |
Same page → Require approvals (set to 1 or 2). |
Auto-assign reviewers |
Use the auto-assign action or GitHub’s built-in CODEOWNERS file. |
PR labelling |
Use actions/labeler to auto-label PRs by file path. |
This is also where CI starts paying for itself as a team practice. With five people committing to one repository, a protected main is what stops a broken build from blocking everyone else’s work an hour before a deadline.
Continuous deployment — “ship it automatically”#
Purpose: Once CI passes, get the new version onto Render without anyone clicking anything.
You have three options, and the cheapest one is probably the right one:
Approach |
Actions minutes |
When to use it |
|---|---|---|
Render auto-deploy on commit |
None — Render watches the branch itself |
The sensible default while you’re building |
Render auto-deploy after CI checks pass |
None |
Once you have branch protection: Render waits for your green checks, then deploys |
Deploy hook called from a workflow |
A few seconds |
When you need to deploy something Render can’t detect on its own — most usefully, a release tag |
The second option is worth setting up properly: it’s configured on the service’s Settings page in the Render dashboard, and Render waits for the new commit’s CI checks to complete before triggering a deploy. In other words:
push to main
|
v
GitHub Actions
|
+-- Checkstyle
+-- tests
+-- package
|
v
all checks pass
|
v
Render deploys
This creates a useful separation of responsibilities:
GitHub Actions answers: “Is this version good enough to deploy?”
Render answers: “How do I turn that version into a running service?”
Render documents this behaviour as After CI Checks Pass auto-deploy. See Render’s deploy documentation.
Recovery and rollback — “handling deployment/application failures”#
Purpose: Quickly restore back to a stable working version when a failure occurs.
A successful CI/CD automate the deployment as well as make failure detection, recovery, and rollback fast and repeatable. The CI/CD pipeline does not gaurantee that the applicatin will work correctly in all the time. Always keep previous stable versions identifiable using Git tags. In production environments, verifying the deployed product is essential. If the verification failes, rollback to the last stable version, fix the problem in current code, test, and redeploy should perform.
Your Actions minutes budget#
A shared, finite pool
GitHub Actions minutes are a shared pool across the whole subject — roughly 200 minutes per team per month. Your repositories are private, so every minute a workflow runs is drawn from it. If the pool is exhausted, deployments stop for every team in the subject, and wasteful CI can be raised at your oral assessment.
This constraint is not an obstacle to good CI — it’s the reason to write good CI. Consider a naive pipeline with three jobs (lint, test, build). Each one gets a fresh runner, so each one checks out the repository, installs a JDK, and downloads your entire dependency tree from Maven Central before doing any useful work. Call it three minutes per run. Five teammates pushing fifteen times a week comes to about 45 minutes a week — and you’re out of minutes before the end of the month.
The same checks in a single cached job take well under a minute. Six levers, roughly in order of impact:
Maven’s lifecycle already runs quality checks, tests and packaging in order. mvn -B verify does all three in one JVM on one runner — one checkout, one JDK setup, one dependency resolution.
~/.m2actions/setup-java will do it for you with cache: maven. Downloading dependencies is most of a cold build; caching turns minutes into seconds.
A concurrency group with cancel-in-progress: true stops the previous run when you push a fix thirty seconds later. Nobody needs CI results for a commit that’s already been replaced.
If you trigger on both push and pull_request for the same branch, a PR from a branch in your own repo runs the pipeline twice. Trigger push on main only, and let pull_request cover everything else.
Wiki edits, README changes and documentation don’t need a Java build. paths-ignore skips the run entirely.
Render can watch your branch itself, at no cost in Actions minutes. Reserve workflow-triggered deploys for the cases Render can’t handle, like tags.
You can see what you’ve spent under Settings → Billing on the organisation, and each run shows its duration in the Actions tab. Check it occasionally — a pipeline that quietly doubled in cost is much easier to fix in Week 6 than in Week 12.
Hands-on: build your pipeline step by step#
Work through the steps below in your team repository. Steps 1 to 5 build the pipeline up one stage at a time so you can see each part fail and pass; Step 6 then consolidates them into the single efficient job you’ll actually keep.
Step 1 — Hello World workflow#
Goal: Verify that GitHub Actions is working in your repo.
Create the folder
.github/workflows/in your repository.Add a file called
ci.ymlwith the following content:
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
hello:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Hello CI
run: echo "CI pipeline is running on branch ${{ github.ref_name }}"
Commit and push.
✅ Checkpoint
Open the Actions tab on GitHub. You should see a green check mark and the log message.
Step 2 — Code quality (Checkstyle)#
Goal: Add automated style checking to your pipeline.
First, add the plugin to your pom.xml and bind it to the validate phase, so it runs early and fails fast:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<consoleOutput>true</consoleOutput>
<failOnViolation>true</failOnViolation>
<violationSeverity>warning</violationSeverity>
</configuration>
<executions>
<execution>
<id>checkstyle-validate</id>
<phase>validate</phase>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
Add a checkstyle.xml rule set at the root of the repository — starting from Google’s or Sun’s published checks and deleting the rules your team disagrees with is a perfectly respectable approach, and a faster route to consensus than writing one from scratch.
Then replace the hello job in ci.yml:
jobs:
lint:
name: Checkstyle
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
cache: maven
- name: Run Checkstyle
run: mvn -B --no-transfer-progress checkstyle:check
What the flags do
-B (batch mode) stops Maven emitting interactive progress, and --no-transfer-progress suppresses the download chatter. Together they turn thousands of lines of CI log into something you can actually read when a build fails.
Try it:
Push code with a deliberate style violation → watch the pipeline fail.
Fix the violation → push again → pipeline turns green.
Look at the run time of the second run compared with the first. The Maven cache should have made it noticeably faster.
Step 3 — Automated testing#
Goal: Run your JUnit suite on every push and pull request.
test:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
cache: maven
- name: Run tests
run: mvn -B --no-transfer-progress test
Surefire picks up anything named *Test.java automatically. If you’ve added Failsafe for integration tests (*IT.java), those run under mvn verify instead — which is what Step 6 will use.
Try it:
Write a simple test that passes → push → green.
Break the test intentionally → push → red.
Fix it → push → green again.
Step 4 — Build the deployable artifact#
Goal: Ensure the deployable artifact builds in a clean environment.
Your application is packaged by Maven and then copied into a Docker image, which is what Render runs. On the setup in the course notes that artefact is a .war, produced by the default package phase. No extra plugin configuration is required.
:class: dropdown
You will need the Shade plugin to produce a single self-contained JAR. Everything else in this workshop is unchanged; substitute *.jar for *.war in the artifact paths below.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.6.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>au.edu.unimelb.swen90007.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
And the job that builds and keeps it:
build:
name: Package
runs-on: ubuntu-latest
needs: [lint, test]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
cache: maven
- name: Package
run: mvn -B --no-transfer-progress package -DskipTests
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: app-artifact
path: target/*.war # or target/*.jar if you build an executable JAR
retention-days: 5
✅ Checkpoint
Download the artefact from the run summary page. It is the same file your Dockerfile copies into the image: build the image locally with docker build, run it, and what starts up is what Render will run.
Step 5 — Branch protection & PR checks#
Goal: Prevent merging code that hasn’t passed the pipeline.
This step is done in GitHub Settings, not in a YAML file:
Go to your repository → Settings → Branches.
Click Add branch protection rule.
Set Branch name pattern to
main(and optionallydevelop).Enable the following:
✅ Require a pull request before merging
✅ Require approvals — set to at least 1
✅ Require status checks to pass before merging — search for and select your CI job names
✅ Require branches to be up to date before merging
Click Create / Save changes.
✅ Checkpoint
Create a PR with a failing test. You should see the merge button blocked with a message indicating the required checks have not passed.
Step 6 — Consolidate into one job#
Goal: Get the same coverage for a fraction of the minutes.
You now have three jobs doing three checkouts, three JDK installs and three dependency resolutions. Because Checkstyle is bound to validate, tests run at test, and Maven packages the application during the normal lifecycle, a single mvn verify performs all of it in order — and stops at the first failure, exactly as the separate jobs did.
Replace the whole of ci.yml:
name: CI
on:
push:
branches: [main]
paths-ignore: ['**.md', 'docs/**']
pull_request:
paths-ignore: ['**.md', 'docs/**']
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
verify:
name: Checkstyle, test & package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
cache: maven
- name: Verify
run: mvn -B --no-transfer-progress verify
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: app-artifact
path: target/*.war
retention-days: 5
For teams using an executable JAR, use target/*.jar instead and remove the WAR-specific path.
Note the trigger change: push now fires on main only, while pull_request covers every branch. Previously a PR from a branch in your own repository ran the pipeline twice for the same commit.
✅ Checkpoint
Compare the total run time against the three-job version in the Actions tab, then update your branch protection rule — the required status check is now verify, and your old job names no longer exist. A protection rule pointing at a deleted job blocks every merge, so fix this before you go home.
Try it:
Push a change to a Markdown file only. No workflow should run at all.
Push twice in quick succession. The first run should be cancelled automatically.
Step 7 — (Optional) Deploy on a release tag#
Goal: Have your submission tag deploy itself.
Your submissions are release tags of the form SWEN90007_2026_Part1A_<team name>. A workflow that deploys when such a tag is pushed means the deployed application and the tagged code can’t drift apart.
First, create a deploy hook: in the Render dashboard, open your service → Settings → Deploy Hook, and copy the URL. Then add it to GitHub under Settings → Secrets and variables → Actions → New repository secret, named RENDER_DEPLOY_HOOK_URL.
A deploy hook can deploy a specific commit by passing the commit SHA as the ref query parameter. This is useful for release tags because the tag identifies an exact version rather than just asking Render to deploy whatever commit currently happens to be latest on the branch. Render documents ref as the mechanism for deploying a specific Git commit. See Render Deploy Hooks.
A minimal workflow is:
name: Deploy
on:
push:
tags: ['SWEN90007_2026_Part*']
jobs:
deploy:
name: Deploy release
runs-on: ubuntu-latest
steps:
- name: Deploy tagged commit
env:
DEPLOY_HOOK: ${{ secrets.RENDER_DEPLOY_HOOK_URL }}
COMMIT_SHA: ${{ github.sha }}
run: |
curl -fsS -X POST \
"${DEPLOY_HOOK}&ref=${COMMIT_SHA}"
The exact deploy-hook URL stored in RENDER_DEPLOY_HOOK_URL already contains the service-specific secret key, so the workflow only appends the ref parameter.
Why deploy the exact commit?
A release tag is meant to identify a specific version. Deploying that commit makes the relationship explicit:
release tag → commit SHA → Render deployment
Without this distinction, “deploy on tag” can become only a trigger for Render to deploy the latest commit on its linked branch.
Anyone with the hook URL can deploy your service
The deploy hook needs no other authentication, so treat it like a password: it belongs in GitHub Secrets and nowhere else — not in ci.yml, not in your Wiki, not in a screenshot in your report. Passing it through env: rather than interpolating it directly into the run: line keeps it out of the workflow logs.
Note about automatic deploys
When you deliberately deploy a specific commit using a Render deploy hook, Render documents that automatic deploys are disabled for the service. This is important because an automatic deploy of the newest branch commit could otherwise replace the version you intentionally selected. If you use this release-tag pattern, decide explicitly whether the service should be tag-driven or branch-driven rather than mixing both behaviours.
Quick reference#
Complete ci.yml — Java / Maven
name: CI
on:
push:
branches: [main]
paths-ignore: ['**.md', 'docs/**']
pull_request:
paths-ignore: ['**.md', 'docs/**']
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
verify:
name: Checkstyle, test & package
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
cache: maven
- name: Verify
run: mvn -B --no-transfer-progress verify
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: app-artifact
path: target/*.war
retention-days: 5
For teams using an executable JAR, replace the artifact path with target/*.jar.
Additional job — React front-end
Only for teams that chose React. This assumes the front-end lives in a frontend/ directory; the paths filter means it runs only when front-end code actually changes, and the Java job’s paths-ignore can be extended with 'frontend/**' so the two don’t trigger each other. ESLint uses for the JavaScript/TypeScript code checks.
frontend:
name: Front-end lint, test & build
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: frontend/package-lock.json
- run: npm ci
- run: npx eslint .
- run: npm test -- --run
- run: npm run build
JSP teams need nothing here — your views are part of the Maven build and are covered by mvn verify.
Deploy on tag — Render
name: Deploy
on:
push:
tags: ['SWEN90007_2026_Part*']
jobs:
deploy:
name: Trigger Render deploy
runs-on: ubuntu-latest
steps:
- name: Call deploy hook and deploy tagged commit
env:
DEPLOY_HOOK: ${{ secrets.RENDER_DEPLOY_HOOK_URL }}
COMMIT_SHA: ${{ github.sha }}
run: |
curl -fsS -X POST \
"${DEPLOY_HOOK}&ref=${COMMIT_SHA}"
This workflow asks Render to deploy the commit represented by the release tag rather than simply deploying the latest commit on the service’s linked branch.
Plugin versions
The plugin versions in this workshop were current when it was written. Pin a version for every plugin — an unpinned plugin resolves differently over time and produces builds that fail for one teammate and not another — and check Maven Central for the current release when you add one.
Expected outcome & checklist#
By the end of this workshop, your team repository should have:
A
.github/workflows/ci.ymlfile committed to your repo.Code quality stage — Checkstyle runs on every push/PR and fails the build on violations.
Test stage — at least one JUnit test runs automatically.
Build stage — the deployable artifact packages successfully in a clean CI environment.
Docker packaging understood — the team can explain how the Maven artifact becomes the runtime image Render runs.
Branch protection —
mainrequires passing CI checks before merge.A consolidated job with Maven caching, a
concurrencygroup, and no duplicatepush/pull_requestruns.CI-gated deployment understood — Render can wait for CI checks to pass before deploying.
PRs show ✅ green checks or ❌ red crosses providing instant feedback.
(Stretch goal) Deployment to Render on merge to
mainor on a release tag.
Record in your Wiki which of these you’ve done and why — the pipeline is part of what’s assessed in Parts 2 and 3, and you’ll be asked to walk through it at the oral.
Expected repository structure#
your-repo/
├── .github/
│ └── workflows/
│ ├── ci.yml ← your pipeline definition
│ └── deploy.yml ← optional, deploy on tag
├── src/
│ ├── main/java/ ← application code
│ └── test/java/ ← JUnit tests
├── frontend/ ← React teams only
├── Dockerfile ← packages the Maven artifact into the runtime image
├── checkstyle.xml ← your agreed rule set
├── pom.xml ← dependencies and plugins
└── README.md
Further reading#
Official documentation#
Resource |
Link |
|---|---|
GitHub Actions documentation |
|
Workflow syntax reference |
https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions |
|
|
GitHub Marketplace (Actions) |
Maven plugins#
Plugin |
Link |
|---|---|
Checkstyle |
|
Surefire (unit tests) |
|
Failsafe (integration tests) |
|
Shade (fat JAR - optional) |
|
JaCoCo (coverage) |
Deployment and testing#
Resource |
Link |
|---|---|
Render — deploying and auto-deploy settings |
|
Render — deploy hooks |
|
Render — Docker deployment |
|
Testcontainers for Java |
|
GitHub’s official starter workflows |