Skip to content

Commit bc27c3f

Browse files
authored
Merge pull request #4 from ACCESS-NRI/2-ci-cd
Add CI/CD to ACCESS-OM3
2 parents a7c9696 + b9203b3 commit bc27c3f

File tree

4 files changed

+360
-0
lines changed

4 files changed

+360
-0
lines changed

.github/workflows/cd.yml

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: CD
2+
on:
3+
push:
4+
branches:
5+
- main
6+
- backport/*.*
7+
paths:
8+
- config/**
9+
- spack.yaml
10+
env:
11+
SPACK_YAML_MODEL_YQ: .spack.specs[0]
12+
jobs:
13+
push-tag:
14+
name: Tag Deployment
15+
runs-on: ubuntu-latest
16+
permissions:
17+
contents: write
18+
outputs:
19+
name: ${{ steps.tag.outputs.name }}
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- name: Generate Tag
24+
id: tag
25+
# Get the tag name from the `spack.yaml` that was merged into main, which
26+
# is of the form `access-om3@git.<version>`.
27+
run: echo "name=$(yq '${{ env.SPACK_YAML_MODEL_YQ }} | split("@git.") | .[1]' spack.yaml)" >> $GITHUB_OUTPUT
28+
29+
- name: Push Tag
30+
# NOTE: Regarding the config user.name/user.email, see https://github.com/actions/checkout/pull/1184
31+
run: |
32+
git config user.name ${{ vars.GH_ACTIONS_BOT_GIT_USER_NAME }}
33+
git config user.email ${{ vars.GH_ACTIONS_BOT_GIT_USER_EMAIL }}
34+
git tag ${{ steps.tag.outputs.name }} --force
35+
git push --tags --force
36+
37+
deploy-release:
38+
name: Deploy Release
39+
needs:
40+
- push-tag
41+
uses: access-nri/build-cd/.github/workflows/deploy-1-setup.yml@main
42+
with:
43+
ref: ${{ github.ref_name }}
44+
version: ${{ needs.push-tag.outputs.name }}
45+
secrets: inherit
46+
permissions:
47+
contents: write

.github/workflows/ci.yml

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
name: CI
2+
on:
3+
pull_request:
4+
branches:
5+
- main
6+
- backport/*.*
7+
paths:
8+
- config/**
9+
- spack.yaml
10+
env:
11+
SPACK_YAML_MODEL_YQ: .spack.specs[0]
12+
jobs:
13+
validate-json:
14+
name: Validate JSON
15+
uses: access-nri/actions/.github/workflows/validate-json.yml@main
16+
with:
17+
src: "config"
18+
19+
check-config:
20+
name: Check Config Fields
21+
needs:
22+
- validate-json
23+
runs-on: ubuntu-latest
24+
outputs:
25+
spack-packages-version: ${{ steps.spack-packages.outputs.version }}
26+
spack-config-version: ${{ steps.spack-config.outputs.version }}
27+
steps:
28+
- name: Validate spack-packages version
29+
id: spack-packages
30+
uses: access-nri/build-cd/.github/actions/validate-repo-version@main
31+
with:
32+
repo-to-check: spack-packages
33+
34+
- name: Validate spack-config version
35+
id: spack-config
36+
uses: access-nri/build-cd/.github/actions/validate-repo-version@main
37+
with:
38+
repo-to-check: spack-config
39+
40+
check-spack-yaml:
41+
name: Check spack.yaml
42+
runs-on: ubuntu-latest
43+
permissions:
44+
pull-requests: write
45+
steps:
46+
- uses: actions/checkout@v4
47+
with:
48+
fetch-depth: 0
49+
50+
- name: Check Model Version Modified
51+
id: version
52+
run: |
53+
git checkout ${{ github.base_ref }}
54+
base_version=$(yq e '${{ env.SPACK_YAML_MODEL_YQ }}' spack.yaml)
55+
56+
git checkout ${{ github.head_ref }}
57+
current_version=$(yq e '${{ env.SPACK_YAML_MODEL_YQ }}' spack.yaml)
58+
echo "current=${current_version}" >> $GITHUB_OUTPUT
59+
60+
if [[ "${base_version}" == "${current_version}" ]]; then
61+
echo "::warning::The version string hasn't been modified in this PR, but needs to be before merging."
62+
exit 1
63+
fi
64+
65+
- name: Same Model Version Failure Notifier
66+
if: failure() && steps.version.outcome == 'failure'
67+
uses: access-nri/actions/.github/actions/pr-comment@main
68+
with:
69+
comment: |
70+
The model version in the `spack.yaml` has not been updated.
71+
Either update it manually, or comment the following to have it updated and committed automatically:
72+
* `!bump major` for feature releases
73+
* `!bump minor` for bugfixes
74+
75+
- name: Projection Version Matches
76+
# this step checks that the versions of the packages themselves match with the
77+
# names of the modules. For example, [email protected] matches with the
78+
# modulefile access-om3/2023.12.12 (specifically, the version strings match)
79+
run: |
80+
FAILED='false'
81+
DEPS=$(yq ".spack.modules.default.tcl.include | join(\" \")" spack.yaml)
82+
83+
# for each of the modules
84+
for DEP in $DEPS; do
85+
DEP_VER=''
86+
if [[ "$DEP" == "access-om3-virtual" ]]; then
87+
DEP_VER=$(yq '.spack.specs[0] | split("@git.") | .[1]' spack.yaml)
88+
else
89+
DEP_VER=$(yq ".spack.packages.\"$DEP\".require[0] | split(\"@git.\") | .[1]" spack.yaml)
90+
fi
91+
92+
MODULE_VER=$(yq ".spack.modules.default.tcl.projections.\"$DEP\" | split(\"/\") | .[1]" spack.yaml)
93+
94+
if [[ "$DEP_VER" != "$MODULE_VER" ]]; then
95+
echo "::error::Version of dependency and projection do not match ($DEP_VER != $MODULE_VER)"
96+
FAILED='true'
97+
fi
98+
done
99+
if [[ "$FAILED" == "true" ]]; then
100+
exit 1
101+
fi
102+
103+
version-tag:
104+
name: Get Version and Tag Prerelease
105+
needs:
106+
- check-spack-yaml
107+
runs-on: ubuntu-latest
108+
permissions:
109+
contents: write
110+
outputs:
111+
release: ${{ steps.version.outputs.release }}
112+
prerelease: ${{ steps.version.outputs.prerelease }}
113+
steps:
114+
- uses: actions/checkout@v4
115+
with:
116+
ref: ${{ github.head_ref }}
117+
fetch-depth: 0
118+
119+
- name: Generate Versions
120+
id: version
121+
# This step generates the release and prerelease version numbers.
122+
# The release is a general version number from the spack.yaml, looking the
123+
# same as a regular release build. Ex. '[email protected]' -> '2024.01.1'
124+
# The prerelease looks like: `pr<pull request number>-<number of commits on this branch>`.
125+
# Ex. Pull Request #12 with 2 commits on branch -> `pr12-2`.
126+
run: |
127+
echo "release=$(yq '${{ env.SPACK_YAML_MODEL_YQ }} | split("@git.") | .[1]' spack.yaml)" >> $GITHUB_OUTPUT
128+
129+
number_of_commits=$(git rev-list --count ${{ github.event.pull_request.base.sha }}..HEAD)
130+
echo "prerelease=pr${{ github.event.pull_request.number }}-${number_of_commits}" >> $GITHUB_OUTPUT
131+
132+
- name: Shift Prerelease Tag ${{ steps.version.outputs.release }}
133+
# We shift the 'Release' tag along the PR as the spack.yaml will not work without the correct tag in this repostiory.
134+
# NOTE: Regarding the config user.name/user.email, see https://github.com/actions/checkout/pull/1184
135+
run: |
136+
git config user.name ${{ vars.GH_ACTIONS_BOT_GIT_USER_NAME }}
137+
git config user.email ${{ vars.GH_ACTIONS_BOT_GIT_USER_EMAIL }}
138+
git tag ${{ steps.version.outputs.release }} --force
139+
git push --tags --force
140+
141+
# -----------------------------
142+
# | PRERELEASE DEPLOYMENT JOB |
143+
# -----------------------------
144+
prerelease-deploy:
145+
name: Deploy to Prerelease
146+
# This will create a `spack` environment with the name `access-om3-pr<pull request number>-<commit number>`.
147+
# For example, `access-om3-pr13-3` for the deployment based on the third commit on the PR#13.
148+
needs:
149+
- version-tag # implies all the spack.yaml-related checks have passed, has appropriate version for the prerelease build
150+
- check-config # implies all the json-related checks have passed
151+
uses: access-nri/build-cd/.github/workflows/deploy-1-setup.yml@main
152+
with:
153+
type: prerelease
154+
ref: ${{ github.head_ref }}
155+
version: ${{ needs.version-tag.outputs.prerelease }}
156+
secrets: inherit
157+
158+
notifier:
159+
name: Notifier
160+
needs:
161+
- version-tag # implies all the spack.yaml-related checks have passed, has appropriate version for the prerelease build
162+
- check-config # implies all the json-related checks have passed
163+
runs-on: ubuntu-latest
164+
permissions:
165+
pull-requests: write
166+
steps:
167+
- uses: access-nri/actions/.github/actions/pr-comment@main
168+
with:
169+
comment: |
170+
This `${{ github.repository }}` model will be deployed with the following versions:
171+
* `${{ needs.version-tag.outputs.release }}` as a Release (when merged).
172+
* `${{ needs.version-tag.outputs.prerelease }}` as a Prerelease (during this PR). This can be accessed on `Gadi` via `spack` at `/g/data/vk83/prerelease/apps/spack/0.20/spack` once deployed.
173+
174+
It will be deployed using:
175+
* `access-nri/spack-packages` version [`${{ needs.check-config.outputs.spack-packages-version }}`](https://github.com/ACCESS-NRI/spack-packages/releases/tag/${{ needs.check-config.outputs.spack-packages-version }})
176+
* `access-nri/spack-config` version [`${{ needs.check-config.outputs.spack-config-version }}`](https://github.com/ACCESS-NRI/spack-config/releases/tag/${{ needs.check-config.outputs.spack-config-version }})
177+
178+
If this is not what was expected, commit changes to `config/versions.json`.

.github/workflows/comment.yml

+99
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
name: Comment Command
2+
on:
3+
issue_comment:
4+
types:
5+
- created
6+
- edited
7+
env:
8+
RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
9+
SPACK_YAML_MODEL_YQ: .spack.specs[0]
10+
SPACK_YAML_MODEL_PROJECTION_YQ: .spack.modules.default.tcl.projections.access-om3-virtual
11+
jobs:
12+
bump-version:
13+
name: Bump spack.yaml
14+
if: github.event.issue.pull_request && startsWith(github.event.comment.body, '!bump')
15+
runs-on: ubuntu-latest
16+
permissions:
17+
pull-requests: write
18+
env:
19+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20+
steps:
21+
- uses: actions/checkout@v4
22+
with:
23+
fetch-depth: 0
24+
fetch-tags: true
25+
token: ${{ secrets.GH_COMMIT_CHECK_TOKEN }}
26+
27+
- name: Setup
28+
id: setup
29+
# outputs:
30+
# original-version: The version contained within the spack.yaml
31+
# version: The version that will be bumped (could be latest tag instead of original-version)
32+
# bump: The bump type (major, minor or current as specified in the bump-version action)
33+
run: |
34+
# Get the version of access-om3-virtual from the spack.yaml in the PR the comment was written in
35+
gh pr checkout ${{ github.event.issue.number }}
36+
original_version=$(yq e '${{ env.SPACK_YAML_MODEL_YQ }} | split("@git.") | .[1]' spack.yaml)
37+
echo "original-version=${original_version}" >> $GITHUB_OUTPUT
38+
39+
# Validate the comment
40+
if [[ "${{ contains(github.event.comment.body, 'major') }}" == "true" ]]; then
41+
# Compare the current date (year-month) with the latest git tag (year-month)
42+
# to determine the next valid tag. We do this because especially feature-rich
43+
# months might increment the date part beyond the current date.
44+
45+
d="$(date +%Y-%m)-01"
46+
d_s=$(date --date "$d" +%s)
47+
48+
latest_tag=$(git describe --tags --abbrev=0 | tr '.' '-')
49+
tag_date=${latest_tag%-*}-01
50+
tag_date_s=$(date --date "$tag_date" +%s)
51+
52+
echo "Comparing current date ${d} with ${tag_date} (tag looks like ${latest_tag})"
53+
54+
if (( d_s <= tag_date_s )); then
55+
echo "version=${tag_date}" >> $GITHUB_OUTPUT
56+
echo "bump=major" >> $GITHUB_OUTPUT
57+
else
58+
echo "version=${original_version}" >> $GITHUB_OUTPUT
59+
echo "bump=current" >> $GITHUB_OUTPUT
60+
fi
61+
elif [[ "${{ contains(github.event.comment.body, 'minor')}}" == "true" ]]; then
62+
echo "version=${original_version}" >> $GITHUB_OUTPUT
63+
echo "bump=minor" >> $GITHUB_OUTPUT
64+
else
65+
echo "::warning::Usage: `!bump [major|minor]`, got `${{ github.event.comment.body }}`"
66+
exit 1
67+
fi
68+
69+
- name: Bump Version
70+
id: bump
71+
uses: access-nri/actions/.github/actions/bump-version@main
72+
with:
73+
version: ${{ steps.setup.outputs.version }}
74+
versioning-scheme: calver-minor
75+
bump-type: ${{ steps.setup.outputs.bump }}
76+
77+
- name: Update, Commit and Push the Bump
78+
run: |
79+
git config user.name ${{ vars.GH_ACTIONS_BOT_GIT_USER_NAME }}
80+
git config user.email ${{ vars.GH_ACTIONS_BOT_GIT_USER_EMAIL }}
81+
82+
yq -i '${{ env.SPACK_YAML_MODEL_YQ }} = "access-om3-virtual@git.${{ steps.bump.outputs.after }}"' spack.yaml
83+
yq -i '${{ env.SPACK_YAML_MODEL_PROJECTION_YQ }} = "{name}/${{ steps.bump.outputs.after }}"' spack.yaml
84+
git add spack.yaml
85+
git commit -m "spack.yaml: Updated access-om3-virtual package version from ${{ steps.setup.outputs.original-version }} to ${{ steps.bump.outputs.after }}"
86+
git push
87+
88+
- name: Success Notifier
89+
uses: access-nri/actions/.github/actions/pr-comment@main
90+
with:
91+
comment: |
92+
:white_check_mark: Version bumped from `${{ steps.setup.outputs.original-version }}` to `${{ steps.bump.outputs.after }}` :white_check_mark:
93+
94+
- name: Failure Notifier
95+
if: failure()
96+
uses: access-nri/actions/.github/actions/pr-comment@main
97+
with:
98+
comment: |
99+
:x: Failed to bump version or commit changes, see ${{ env.RUN_URL }} :x:

.github/workflows/pr-closed.yml

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: PR Closed Cleanup
2+
# Remove prereleases that were part of a closed PR, so we save space
3+
# on our deployment targets. If needed, one can still get the
4+
# spack.yaml as part of the closed PR and revive it themselves.
5+
on:
6+
pull_request:
7+
types:
8+
- closed
9+
branches:
10+
- main
11+
- backport/*.*
12+
paths:
13+
- config/**
14+
- spack.yaml
15+
jobs:
16+
setup:
17+
name: Setup
18+
runs-on: ubuntu-latest
19+
outputs:
20+
version-pattern: ${{ steps.version.outputs.pattern }}
21+
steps:
22+
- name: Version Pattern
23+
id: version
24+
# For example, `access-om3-pr12-*`
25+
run: |
26+
repo_name_lower=$(echo ${{ github.event.repository.name }} | tr [:upper:] [:lower:])
27+
echo "pattern=${repo_name_lower}-pr${{ github.event.pull_request.number }}-*" >> $GITHUB_OUTPUT
28+
29+
undeploy-prereleases:
30+
name: Undeploy Prereleases
31+
needs:
32+
- setup
33+
uses: access-nri/build-cd/.github/workflows/undeploy-1-setup.yml@main
34+
with:
35+
version-pattern: ${{ needs.setup.outputs.version-pattern }}
36+
secrets: inherit

0 commit comments

Comments
 (0)