@@ -3,202 +3,106 @@ permissions:
3
3
packages : write
4
4
contents : write
5
5
on :
6
- # Triggered on new GitHub Release
7
- release :
8
- types : [published]
9
- # Triggered on every successful Build action
10
6
workflow_run :
11
- workflows : ["Build"]
12
- branches : [main,master]
7
+ workflows : ["Build Container"]
13
8
types :
14
9
- completed
15
- # Manual trigger for rollback to specific release or redeploy latest
10
+ branches :
11
+ - main
12
+ - master
16
13
workflow_dispatch :
17
- inputs :
18
- version :
19
- default : latest
20
- description : Tag you want to release.
21
- required : true
14
+
15
+ env :
16
+ DOCKER_BUILDKIT : 1
17
+ KAMAL_REGISTRY_PASSWORD : ${{ secrets.GITHUB_TOKEN }}
18
+ KAMAL_REGISTRY_USERNAME : ${{ github.actor }}
22
19
23
20
jobs :
24
- push_to_registry :
21
+ release :
25
22
runs-on : ubuntu-latest
26
- if : ${{ github.event.workflow_run.conclusion != 'failure ' }}
23
+ if : ${{ github.event.workflow_run.conclusion == 'success ' }}
27
24
steps :
28
- # Checkout latest or specific tag
29
- - name : checkout
30
- if : ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
31
- uses : actions/checkout@v3
32
- - name : checkout tag
33
- if : ${{ github.event.inputs.version != '' && github.event.inputs.version != 'latest' }}
25
+ - name : Checkout code
34
26
uses : actions/checkout@v3
35
- with :
36
- ref : refs/tags/${{ github.event.inputs.version }}
37
-
38
- # Assign environment variables used in subsequent steps
39
- - name : Env variable assignment
40
- run : echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
41
- # TAG_NAME defaults to 'latest' if not a release or manual deployment
42
- - name : Assign version
27
+
28
+ - name : Set up environment variables
43
29
run : |
44
- echo "TAG_NAME=latest" >> $GITHUB_ENV
45
- if [ "${{ github.event.release.tag_name }}" != "" ]; then
46
- echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
47
- fi;
48
- if [ "${{ github.event.inputs.version }}" != "" ]; then
49
- echo "TAG_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV
50
- fi;
51
- if [ ! -z "${{ secrets.APPSETTINGS_PATCH }}" ]; then
52
- echo "HAS_APPSETTINGS_PATCH=true" >> $GITHUB_ENV
30
+ echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
31
+ echo "repository_name=$(echo ${{ github.repository }} | cut -d '/' -f 2)" >> $GITHUB_ENV
32
+ echo "repository_name_lower=$(echo ${{ github.repository }} | cut -d '/' -f 2 | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
33
+ echo "org_name=$(echo ${{ github.repository }} | cut -d '/' -f 1)" >> $GITHUB_ENV
34
+ if find . -maxdepth 2 -type f -name "Configure.Db.Migrations.cs" | grep -q .; then
35
+ echo "HAS_MIGRATIONS=true" >> $GITHUB_ENV
36
+ else
37
+ echo "HAS_MIGRATIONS=false" >> $GITHUB_ENV
38
+ fi
39
+ if [ -n "${{ secrets.KAMAL_DEPLOY_IP }}" ]; then
40
+ echo "HAS_DEPLOY_ACTION=true" >> $GITHUB_ENV
53
41
else
54
- echo "HAS_APPSETTINGS_PATCH=false" >> $GITHUB_ENV
55
- fi;
56
-
42
+ echo "HAS_DEPLOY_ACTION=false" >> $GITHUB_ENV
43
+ fi
44
+
45
+ # This step is for the deployment of the templates only, safe to delete
46
+ - name : Modify deploy.yml
47
+ if : env.HAS_DEPLOY_ACTION == 'true'
48
+ run : |
49
+ sed -i "s/service: my-app/service: ${{ env.repository_name_lower }}/g" config/deploy.yml
50
+ sed -i "s#image: my-user/myapp#image: ${{ env.image_repository_name }}#g" config/deploy.yml
51
+ sed -i "s/- 192.168.0.1/- ${{ secrets.KAMAL_DEPLOY_IP }}/g" config/deploy.yml
52
+ sed -i "s/host: my-app.example.com/host: ${{ secrets.KAMAL_DEPLOY_HOST }}/g" config/deploy.yml
53
+ sed -i "s/MyApp/${{ env.repository_name }}/g" config/deploy.yml
54
+
57
55
- name : Login to GitHub Container Registry
58
- uses : docker/login-action@v2
56
+ uses : docker/login-action@v3
59
57
with :
60
58
registry : ghcr.io
61
- username : ${{ github.actor }}
62
- password : ${{ secrets.GITHUB_TOKEN }}
63
-
64
-
65
- - name : Setup dotnet
66
- uses : actions/setup-dotnet@v3
59
+ username : ${{ env.KAMAL_REGISTRY_USERNAME }}
60
+ password : ${{ env.KAMAL_REGISTRY_PASSWORD }}
61
+
62
+ - name : Set up SSH key
63
+ uses :
webfactory/[email protected]
67
64
with :
68
- dotnet-version : ' 8.0'
69
-
70
- - name : Install x tool
71
- if : env.HAS_APPSETTINGS_PATCH == 'true'
72
- run : dotnet tool install -g x
73
-
74
- - name : Apply Production AppSettings
75
- if : env.HAS_APPSETTINGS_PATCH == 'true'
76
- working-directory : ./AiServer
77
- run : |
78
- cat <<EOF >> appsettings.json.patch
79
- ${{ secrets.APPSETTINGS_PATCH }}
80
- EOF
81
- x patch appsettings.json.patch
82
-
83
-
84
- # Build and push new docker image, skip for manual redeploy other than 'latest'
85
- - name : Build and push Docker image
86
- run : |
87
- dotnet publish --os linux --arch x64 -c Release -p:ContainerRepository=${{ env.image_repository_name }} -p:ContainerRegistry=ghcr.io -p:ContainerImageTags=${{ env.TAG_NAME }} -p:ContainerPort=80
65
+ ssh-private-key : ${{ secrets.SSH_PRIVATE_KEY }}
88
66
89
- deploy_via_ssh :
90
- needs : push_to_registry
91
- runs-on : ubuntu-latest
92
- if : ${{ github.event.workflow_run.conclusion != 'failure' }}
93
- steps :
94
- # Checkout latest or specific tag
95
- - name : checkout
96
- if : ${{ github.event.inputs.version == '' || github.event.inputs.version == 'latest' }}
97
- uses : actions/checkout@v3
98
- - name : checkout tag
99
- if : ${{ github.event.inputs.version != '' && github.event.inputs.version != 'latest' }}
100
- uses : actions/checkout@v3
67
+ - name : Setup Ruby
68
+ uses : ruby/setup-ruby@v1
101
69
with :
102
- ref : refs/tags/${{ github.event.inputs.version }}
70
+ ruby-version : 3.3.0
71
+ bundler-cache : true
103
72
104
- - name : repository name fix and env
73
+ - name : Install Kamal
74
+ run : gem install kamal -v 2.3.0
75
+
76
+ - name : Set up Docker Buildx
77
+ uses : docker/setup-buildx-action@v3
78
+ with :
79
+ driver-opts : image=moby/buildkit:master
80
+
81
+ - name : Kamal bootstrap
82
+ run : kamal server bootstrap
83
+
84
+ - name : Check if first run and execute kamal app boot if necessary
105
85
run : |
106
- echo "image_repository_name=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
107
- echo "TAG_NAME=latest" >> $GITHUB_ENV
108
- if [ "${{ github.event.release.tag_name }}" != "" ]; then
109
- echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
110
- fi;
111
- if [ "${{ github.event.inputs.version }}" != "" ]; then
112
- echo "TAG_NAME=${{ github.event.inputs.version }}" >> $GITHUB_ENV
113
- fi;
86
+ FIRST_RUN_FILE=".${{ env.repository_name }}"
87
+ if ! kamal server exec --no-interactive -q "test -f $FIRST_RUN_FILE"; then
88
+ kamal server exec --no-interactive -q "touch $FIRST_RUN_FILE" || true
89
+ kamal deploy -q -P --version latest || true
90
+ else
91
+ echo "Not first run, skipping kamal app boot"
92
+ fi
114
93
115
- - name : Create .env file
94
+ - name : Ensure file permissions
116
95
run : |
117
- echo "Generating .env file"
118
-
119
- echo "# Autogenerated .env file" > .deploy/.env
120
- echo "HOST_DOMAIN=${{ secrets.DEPLOY_HOST }}" >> .deploy/.env
121
- echo "LETSENCRYPT_EMAIL=${{ secrets.LETSENCRYPT_EMAIL }}" >> .deploy/.env
122
- echo "APP_NAME=${{ github.event.repository.name }}" >> .deploy/.env
123
- echo "IMAGE_REPO=${{ env.image_repository_name }}" >> .deploy/.env
124
- echo "RELEASE_VERSION=${{ env.TAG_NAME }}" >> .deploy/.env
125
- echo "CIVIT_AI_API_KEY=${{ secrets.CIVIT_AI_API_KEY }}" >> .deploy/.env
126
- echo "REPLICATE_API_KEY=${{ secrets.REPLICATE_API_KEY }}" >> .deploy/.env
127
- echo "GOOGLE_API_KEY=${{ secrets.GOOGLE_API_KEY }}" >> .deploy/.env
128
- echo "GROQ_API_KEY=${{ secrets.GROQ_API_KEY }}" >> .deploy/.env
129
- echo "MISTRAL_API_KEY=${{ secrets.MISTRAL_API_KEY }}" >> .deploy/.env
130
- echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> .deploy/.env
131
- echo "OPENROUTER_API_KEY=${{ secrets.OPENROUTER_API_KEY }}" >> .deploy/.env
132
-
133
- # Copy only the docker-compose.yml to remote server home folder
134
- - name : copy files to target server via scp
135
-
136
- with :
137
- host : ${{ secrets.DEPLOY_HOST }}
138
- username : ${{ secrets.DEPLOY_USERNAME }}
139
- port : 22
140
- key : ${{ secrets.DEPLOY_KEY }}
141
- strip_components : 2
142
- source : " ./.deploy/docker-compose.yml,./.deploy/.env"
143
- target : " ~/.deploy/${{ github.event.repository.name }}/"
144
-
145
- - name : Setup App_Data volume directory
146
-
147
- env :
148
- APPTOKEN : ${{ secrets.GITHUB_TOKEN }}
149
- USERNAME : ${{ secrets.DEPLOY_USERNAME }}
150
- with :
151
- host : ${{ secrets.DEPLOY_HOST }}
152
- username : ${{ secrets.DEPLOY_USERNAME }}
153
- key : ${{ secrets.DEPLOY_KEY }}
154
- port : 22
155
- envs : APPTOKEN,USERNAME
156
- script : |
157
- set -e
158
- echo $APPTOKEN | docker login ghcr.io -u $USERNAME --password-stdin
159
- cd ~/.deploy/${{ github.event.repository.name }}
160
- docker compose pull
161
- export APP_ID=$(docker compose run --entrypoint "id -u" --rm app)
162
- docker compose run --entrypoint "chown $APP_ID:$APP_ID /app/App_Data" --user root --rm app
163
- docker compose run --entrypoint "chown $APP_ID:$APP_ID /app/artifacts" --user root --rm app
164
- docker compose run --entrypoint "chown $APP_ID:$APP_ID /app/files" --user root --rm app
96
+ kamal server exec --no-interactive "mkdir -p /opt/docker/${{ env.repository_name }}/App_Data && chown -R 1654:1654 /opt/docker/${{ env.repository_name }}"
165
97
166
- - name : Run remote db migrations
167
-
168
- env :
169
- APPTOKEN : ${{ secrets.GITHUB_TOKEN }}
170
- USERNAME : ${{ secrets.DEPLOY_USERNAME }}
171
- with :
172
- host : ${{ secrets.DEPLOY_HOST }}
173
- username : ${{ secrets.DEPLOY_USERNAME }}
174
- key : ${{ secrets.DEPLOY_KEY }}
175
- port : 22
176
- envs : APPTOKEN,USERNAME
177
- script : |
178
- set -e
179
- echo $APPTOKEN | docker login ghcr.io -u $USERNAME --password-stdin
180
- cd ~/.deploy/${{ github.event.repository.name }}
181
- docker compose pull
182
- export APP_ID=$(docker compose run --entrypoint "id -u" --rm app)
183
- docker compose run --entrypoint "chown $APP_ID:$APP_ID /app/App_Data" --user root --rm app
184
- docker compose run --entrypoint "chown $APP_ID:$APP_ID /app/artifacts" --user root --rm app
185
- docker compose run --entrypoint "chown $APP_ID:$APP_ID /app/files" --user root --rm app
186
- docker compose up app-migration --exit-code-from app-migration
98
+ - name : Migration
99
+ if : env.HAS_MIGRATIONS == 'true'
100
+ run : |
101
+ kamal server exec --no-interactive 'echo "${{ env.KAMAL_REGISTRY_PASSWORD }}" | docker login ghcr.io -u ${{ env.KAMAL_REGISTRY_USERNAME }} --password-stdin'
102
+ kamal server exec --no-interactive "docker pull ghcr.io/${{ env.image_repository_name }}:latest || true"
103
+ kamal app exec --no-reuse --no-interactive --version=latest "--AppTasks=migrate"
187
104
188
- # Deploy Docker image with your application using `docker compose up` remotely
189
- - name : remote docker-compose up via ssh
190
-
191
- env :
192
- APPTOKEN : ${{ secrets.GITHUB_TOKEN }}
193
- USERNAME : ${{ secrets.DEPLOY_USERNAME }}
194
- with :
195
- host : ${{ secrets.DEPLOY_HOST }}
196
- username : ${{ secrets.DEPLOY_USERNAME }}
197
- key : ${{ secrets.DEPLOY_KEY }}
198
- port : 22
199
- envs : APPTOKEN,USERNAME
200
- script : |
201
- echo $APPTOKEN | docker login ghcr.io -u $USERNAME --password-stdin
202
- cd ~/.deploy/${{ github.event.repository.name }}
203
- docker compose pull
204
- docker compose up app -d
105
+ - name : Deploy with Kamal
106
+ run : |
107
+ kamal lock release -v
108
+ kamal deploy -P --version latest
0 commit comments