Skip to content

Commit 1418ec7

Browse files
authored
Merge pull request #160 from unisoncomputing/task-test
Add CI for Task Runner builds
2 parents ec1c431 + 8769487 commit 1418ec7

File tree

10 files changed

+243
-19
lines changed

10 files changed

+243
-19
lines changed

.github/workflows/ci.yaml

Lines changed: 100 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ on:
1414
branches:
1515
- main
1616
- staging
17+
- 'task-*'
1718
workflow_dispatch:
1819

1920
env:
@@ -56,8 +57,8 @@ jobs:
5657
&& github.event.pull_request.base.repo.full_name == github.event.pull_request.head.repo.full_name
5758
with:
5859
commit_message: automatically run ormolu
59-
build-exe:
60-
name: Build share-api executable
60+
build-exes:
61+
name: Build executables
6162
runs-on: ubuntu-24.04
6263
steps:
6364
- uses: actions/checkout@v4
@@ -83,12 +84,19 @@ jobs:
8384
--copy-bins \
8485
${{ (env.is_published_build && '--ghc-options -O2') || '--fast' }}
8586
86-
- name: Save exes for docker build
87+
- name: Save exes for share-api docker build
8788
uses: actions/upload-artifact@v4
8889
with:
8990
name: share-api-exe
9091
path: ${{env.share_local_bin}}
9192

93+
- name: Save exes for share-task-runner docker build
94+
if: ${{ (github.event_name == 'push' && startsWith(github.ref, 'refs/heads/task-')) || (github.event_name == 'pull_request' && startsWith(github.head_ref, 'task-')) }}
95+
uses: actions/upload-artifact@v4
96+
with:
97+
name: share-task-runner-exe
98+
path: ${{env.share_local_bin}}
99+
92100
- name: save stack caches
93101
if: |
94102
!cancelled()
@@ -97,12 +105,95 @@ jobs:
97105
with:
98106
cache-prefix: ${{env.exe_cache_prefix}}
99107

100-
# A separate job for docker build because it requires elevated github token permissions.
101-
docker-build:
108+
# Separate jobs for the docker builds because they requires elevated github token permissions.
109+
share-task-runner-docker-build:
110+
if: ${{ (github.event_name == 'push' && startsWith(github.ref, 'refs/heads/task-')) || (github.event_name == 'pull_request' && startsWith(github.head_ref, 'task-')) }}
111+
env:
112+
container_registry: ghcr.io
113+
docker_image_name: unisoncomputing/share-task-runner
114+
needs: [build-exes]
115+
runs-on: ubuntu-24.04
116+
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
117+
permissions:
118+
contents: read
119+
# Allow uploading the docker image to the container registry
120+
packages: write
121+
# Allow creating and updating the artifact attestation
122+
attestations: write
123+
# Required to get user information for building attestations
124+
id-token: write
125+
126+
steps:
127+
- uses: actions/checkout@v4
128+
with:
129+
# Don't need unison submodule for docker image build
130+
submodules: false
131+
132+
# Downloads the artifact that contains the share-api-exe from the previous job.
133+
- uses: actions/download-artifact@v4
134+
with:
135+
name: share-task-runner-exe
136+
path: ./docker/tmp/
137+
138+
# Configure Docker's builder,
139+
# This seems necessary to support docker cache layers.
140+
- name: Setup Docker buildx
141+
uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0
142+
143+
144+
# Uses the `docker/login-action` action to log in to the Container registry registry using the account and password that will publish the packages. Once published, the packages are scoped to the account defined here.
145+
- name: Log in to the Container registry
146+
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20
147+
with:
148+
registry: ${{ env.container_registry }}
149+
username: ${{ github.actor }}
150+
password: ${{ secrets.GITHUB_TOKEN }}
151+
152+
# This step uses [docker/metadata-action](https://github.com/docker/metadata-action#about) to extract tags and labels that will be applied to the specified image. The `id` "meta" allows the output of this step to be referenced in a subsequent step. The `images` value provides the base name for the tags and labels.
153+
- name: Extract metadata (tags, labels) for Docker
154+
id: meta
155+
uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7
156+
with:
157+
images: ${{ env.container_registry }}/${{ env.docker_image_name }}
158+
tags: |
159+
type=sha,priority=1000,format=short,prefix={{branch}}_{{date 'YYYY-MM-DD-HH-mm'}}_gitref-
160+
type=sha,format=long
161+
162+
# This step uses the `docker/build-push-action` action to build the image, based on your repository's `Dockerfile`. If the build succeeds, it pushes the image to GitHub Packages.
163+
# It uses the `context` parameter to define the build's context as the set of files located in the specified path. For more information, see "[Usage](https://github.com/docker/build-push-action#usage)" in the README of the `docker/build-push-action` repository.
164+
# It uses the `tags` and `labels` parameters to tag and label the image with the output from the "meta" step.
165+
- name: Build and push Docker image
166+
id: push
167+
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0
168+
with:
169+
context: ./docker/
170+
file: ./docker/share-task-runner.Dockerfile
171+
push: true
172+
tags: ${{ steps.meta.outputs.tags }}
173+
labels: ${{ steps.meta.outputs.labels }}
174+
# Use github actions cache for docker image layers
175+
cache-from: type=gha
176+
cache-to: type=gha,mode=max
177+
build-args: |
178+
SHARE_COMMIT=${{ github.sha }}
179+
# Save image locally for use in tests even if we don't push it.
180+
outputs: type=docker,dest=/tmp/share-docker-image.tar # export docker image
181+
182+
# This step generates an artifact attestation for the image, which is an unforgeable statement about where and how it was built. It increases supply chain security for people who consume the image. For more information, see "[AUTOTITLE](/actions/security-guides/using-artifact-attestations-to-establish-provenance-for-builds)."
183+
- name: Generate artifact attestation
184+
uses: actions/[email protected]
185+
with:
186+
subject-name: ${{ env.container_registry }}/${{ env.docker_image_name}}
187+
subject-digest: ${{ steps.push.outputs.digest }}
188+
push-to-registry: true
189+
190+
191+
# Separate jobs for the docker builds because they requires elevated github token permissions.
192+
share-api-docker-build:
102193
env:
103194
container_registry: ghcr.io
104-
docker_image_name: ${{ github.repository }}
105-
needs: [build-exe]
195+
docker_image_name: unisoncomputing/share-api
196+
needs: [build-exes]
106197
runs-on: ubuntu-24.04
107198
# Sets the permissions granted to the `GITHUB_TOKEN` for the actions in this job.
108199
permissions:
@@ -158,6 +249,7 @@ jobs:
158249
uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0
159250
with:
160251
context: ./docker/
252+
file: ./docker/share-api.Dockerfile
161253
push: ${{ env.is_published_build }}
162254
tags: ${{ steps.meta.outputs.tags }}
163255
labels: ${{ steps.meta.outputs.labels }}
@@ -186,7 +278,7 @@ jobs:
186278

187279
# A separate job for docker build because it requires elevated github token permissions.
188280
transcript-tests:
189-
needs: [build-exe, docker-build]
281+
needs: [build-exes, share-api-docker-build]
190282
runs-on: ubuntu-24.04
191283

192284
steps:
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ RUN openssl genrsa -out /etc/ssl/private/share.key &&\
66
update-locale LANG=C.UTF-8
77
ENV LANG=C.UTF-8
88

9-
COPY share-entrypoint.sh /usr/local/bin/share-entrypoint
10-
RUN chmod 555 /usr/local/bin/share-entrypoint
9+
COPY share-api-entrypoint.sh /usr/local/bin/share-api-entrypoint
10+
RUN chmod 555 /usr/local/bin/share-api-entrypoint
1111

1212
COPY tmp/share-api /usr/local/bin/share
1313
RUN chmod 555 /usr/local/bin/share
1414

15-
ENTRYPOINT /usr/local/bin/share-entrypoint
15+
ENTRYPOINT /usr/local/bin/share-api-entrypoint
1616

1717
ARG SHARE_COMMIT
1818
ENV SHARE_COMMIT=$SHARE_COMMIT
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/sh
2+
3+
set -ex
4+
5+
echo SHARE_REDIS: "$SHARE_REDIS"
6+
7+
if [ -n "$NOMAD_PORT_enlil_http" ]; then
8+
export SHARE_SERVER_PORT="$NOMAD_PORT_enlil_http"
9+
fi
10+
11+
export SHARE_IP=0.0.0.0
12+
13+
exec 2>&1
14+
exec /usr/local/bin/share-task-runner
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
FROM debian:trixie
2+
RUN apt-get update && apt-get install -y ssl-cert libpq5 ca-certificates curl locales
3+
RUN openssl genrsa -out /etc/ssl/private/share.key &&\
4+
echo "C.UTF-8 UTF-8" > /etc/locale.gen &&\
5+
dpkg-reconfigure --frontend=noninteractive locales &&\
6+
update-locale LANG=C.UTF-8
7+
ENV LANG=C.UTF-8
8+
9+
COPY share-task-runner-entrypoint.sh /usr/local/bin/share-task-runner-entrypoint
10+
RUN chmod 555 /usr/local/bin/share-task-runner-entrypoint
11+
12+
COPY tmp/share-task-runner /usr/local/bin/share-task-runner
13+
RUN chmod 555 /usr/local/bin/share-task-runner
14+
15+
ENTRYPOINT /usr/local/bin/share-task-runner-entrypoint
16+
17+
ARG SHARE_COMMIT
18+
ENV SHARE_COMMIT=$SHARE_COMMIT

share-api/src/Share/Utils/Tags.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module Share.Utils.Tags
88
where
99

1010
import Control.Monad.Trans.Maybe (mapMaybeT)
11+
import Control.Monad.Writer.CPS
1112
import Share.Prelude
1213

1314
type Tags = Map Text Text
@@ -35,6 +36,10 @@ instance (MonadTags m) => MonadTags (ReaderT e m) where
3536
askTags = lift askTags
3637
withTags newTags = mapReaderT (withTags newTags)
3738

39+
instance (Monoid w, MonadTags m) => MonadTags (WriterT w m) where
40+
askTags = lift askTags
41+
withTags newTags = mapWriterT (withTags newTags)
42+
3843
instance (MonadTags m) => MonadTags (MaybeT m) where
3944
askTags = lift askTags
4045
withTags newTags = mapMaybeT (withTags newTags)

share-task-runner/app/Main.hs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
module Main where
22

3-
import Share.Env (withEnv)
43
import Share.BackgroundJobs.Monad
5-
import UnliftIO
4+
import Share.Env (withEnv)
5+
import Share.Tasks.AmbiguousComponentCheck qualified as AmbiguousComponentCheck
66

77
main :: IO ()
88
main = do
9-
withEnv \env -> runBackground env "share-task-runner" task
10-
11-
task :: Background ()
12-
task = do
13-
liftIO $ putStrLn "Hello from the task runner!"
14-
pure ()
9+
withEnv \env -> runBackground env "share-task-runner" AmbiguousComponentCheck.run

share-task-runner/package.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ dependencies:
3636
- base >= 4.7 && < 5
3737
- share-api
3838
- unliftio
39+
- unison-share-api
40+
- unison-codebase-sqlite
41+
- unison-hash
42+
- serialise
43+
- transformers
44+
- cborg
3945

4046
default-extensions:
4147
- ApplicativeDo

share-task-runner/share-task-runner.cabal

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ source-repository head
2121
location: https://github.com/unisoncomputing/share-task-runner
2222

2323
library
24+
exposed-modules:
25+
Share.Tasks.AmbiguousComponentCheck
2426
hs-source-dirs:
2527
src
2628
default-extensions:
@@ -57,7 +59,13 @@ library
5759
ghc-options: -Wall -Werror -Wname-shadowing -Wno-type-defaults -Wno-missing-pattern-synonym-signatures -Wincomplete-uni-patterns -Widentities -Wredundant-constraints -Wpartial-fields -fprint-expanded-synonyms -fwrite-ide-info -O2 -funbox-strict-fields
5860
build-depends:
5961
base >=4.7 && <5
62+
, cborg
63+
, serialise
6064
, share-api
65+
, transformers
66+
, unison-codebase-sqlite
67+
, unison-hash
68+
, unison-share-api
6169
, unliftio
6270
default-language: Haskell2010
6371

@@ -99,7 +107,13 @@ executable share-task-runner
99107
ghc-options: -Wall -Werror -Wname-shadowing -Wno-type-defaults -Wno-missing-pattern-synonym-signatures -Wincomplete-uni-patterns -Widentities -Wredundant-constraints -Wpartial-fields -fprint-expanded-synonyms -fwrite-ide-info -O2 -funbox-strict-fields -threaded -rtsopts "-with-rtsopts=-N -A32m -qn2 -T"
100108
build-depends:
101109
base >=4.7 && <5
110+
, cborg
111+
, serialise
102112
, share-api
103113
, share-task-runner
114+
, transformers
115+
, unison-codebase-sqlite
116+
, unison-hash
117+
, unison-share-api
104118
, unliftio
105119
default-language: Haskell2010
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
module Share.Tasks.AmbiguousComponentCheck (run) where
2+
3+
import Codec.Serialise qualified as CBOR
4+
import Share.BackgroundJobs.Errors (reportError)
5+
import Share.BackgroundJobs.Monad
6+
import Share.Postgres qualified as PG
7+
import Share.Postgres.Cursors qualified as PG
8+
import Share.Prelude
9+
import Share.Utils.Logging (Loggable (..))
10+
import Share.Utils.Logging qualified as Logging
11+
import U.Codebase.Sqlite.TempEntity
12+
import Unison.Hash32
13+
import Unison.Sync.EntityValidation qualified as EV
14+
import Unison.Sync.Types qualified as Sync
15+
import Unison.Util.Servant.CBOR
16+
import Unison.Util.Servant.CBOR qualified as CBOR
17+
18+
data AmbiguousComponentCheckError
19+
= TaskAmbiguousComponentCheckError Hash32
20+
| TaskEntityValidationError Hash32 Sync.EntityValidationError
21+
| TaskEntityDecodingError Hash32 CBOR.DeserialiseFailure
22+
deriving (Show, Eq)
23+
24+
instance Loggable AmbiguousComponentCheckError where
25+
toLog = \case
26+
TaskAmbiguousComponentCheckError hash32 ->
27+
Logging.textLog ("Ambiguous component found for hash: " <> into @Text hash32)
28+
& Logging.withSeverity Logging.Error
29+
TaskEntityValidationError hash32 validationError ->
30+
Logging.textLog
31+
( "Entity validation error for hash: "
32+
<> into @Text hash32
33+
<> ", error: "
34+
<> into @Text (show validationError)
35+
)
36+
& Logging.withSeverity Logging.Error
37+
TaskEntityDecodingError hash32 decodeError ->
38+
Logging.textLog
39+
( "Entity decoding error for hash: "
40+
<> into @Text hash32
41+
<> ", error: "
42+
<> into @Text (show decodeError)
43+
)
44+
& Logging.withSeverity Logging.Error
45+
46+
run :: Background ()
47+
run = withWorkerName "ambiguous-component-task" do
48+
Logging.logInfoText "Starting ambiguous component check task."
49+
errs <- PG.runTransaction $ do
50+
cursor <-
51+
PG.newRowCursor @(CBORBytes TempEntity, Hash32)
52+
"component_cursor"
53+
[PG.sql|
54+
(SELECT DISTINCT ON (t.component_hash_id) bytes.bytes, ch.base32
55+
FROM terms t
56+
JOIN serialized_components sc ON t.component_hash_id = sc.component_hash_id
57+
JOIN bytes ON sc.bytes_id = bytes.id
58+
JOIN component_hashes ch ON t.component_hash_id = ch.id
59+
)
60+
|]
61+
PG.foldBatched cursor 100 \rows -> do
62+
rows
63+
& foldMap
64+
( \(bytes, hash32) -> do
65+
case unpackEntity bytes of
66+
Left err -> [TaskEntityDecodingError hash32 err]
67+
Right entity -> do
68+
case EV.validateTempEntity hash32 entity of
69+
Nothing -> []
70+
Just validationError -> [TaskEntityValidationError hash32 validationError]
71+
)
72+
& pure
73+
for_ errs reportError
74+
Logging.logInfoText "Finished ambiguous component check task."
75+
76+
unpackEntity :: (CBORBytes TempEntity) -> Either CBOR.DeserialiseFailure TempEntity
77+
unpackEntity entityBytes = do
78+
case CBOR.deserialiseOrFailCBORBytes entityBytes of
79+
Left err -> Left err
80+
Right entity -> Right entity

0 commit comments

Comments
 (0)