diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..7fb53e1
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,40 @@
+name: CI
+
+on: [push]
+
+jobs:
+ build:
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up JDK 1.8
+ uses: actions/setup-java@v1
+ with:
+ java-version: 1.8
+ - name: Get current date
+ id: date
+ run: echo "::set-output name=date::$(date +'%Y%m%d%H%M')"
+ - name: Build with Ant
+ run: ant -noinput -buildfile build.xml -Dtimestamp=${{steps.date.outputs.date}}
+ - name: Create Release
+ id: create_release
+ uses: actions/create-release@v1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{steps.date.outputs.date}}
+ release_name: ${{steps.date.outputs.date}}
+ draft: false
+ prerelease: false
+ - name: Upload Release Asset
+ id: upload-release-asset
+ uses: actions/upload-release-asset@v1.0.1
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ upload_url: ${{ steps.create_release.outputs.upload_url }}
+ asset_path: dist/CommunityModules-${{steps.date.outputs.date}}.jar
+ asset_name: CommunityModules-${{steps.date.outputs.date}}.jar
+ asset_content_type: application/zip
\ No newline at end of file
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
deleted file mode 100644
index 797c71c..0000000
--- a/azure-pipelines.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-trigger:
- - master
-
-pool:
- vmImage: 'Ubuntu-16.04'
-
-steps:
- - task: Ant@1
- inputs:
- buildFile: 'build.xml'
- javaHomeOption: 'JDKVersion'
- jdkVersionOption: '1.8'
- jdkArchitectureOption: 'x64'
-
- - task: GitHubRelease@0
- inputs:
- gitHubConnection: CommunityModules
- #repositoryName: '$(Build.Repository.Name)'
- #action: 'create' # Options: create, edit, delete
- #target: '$(Build.SourceVersion)' # Required when action == Create || Action == Edit
- tagSource: 'manual' # Required when action == Create# Options: auto, manual
- tag: '$(Build.BuildNumber)' # Required when action == Edit || Action == Delete || TagSource == Manual
- #title: # Optional
- #releaseNotesSource: 'file' # Optional. Options: file, input
- #releaseNotesFile: # Optional
- #releaseNotes: # Optional
- assets: 'dist/CommunityModules-*.jar' # Optional
- #assetUploadMode: 'delete' # Optional. Options: delete, replace
- #isDraft: false # Optional
- #isPreRelease: false # Optional
- #addChangeLog: true # Optional
diff --git a/build.xml b/build.xml
index 60df739..ed40171 100644
--- a/build.xml
+++ b/build.xml
@@ -6,6 +6,12 @@
+
+
+
+
@@ -25,7 +31,7 @@
-
+
-
+
diff --git a/modules/IOUtils.tla b/modules/IOUtils.tla
index 3dd486a..17f09b1 100644
--- a/modules/IOUtils.tla
+++ b/modules/IOUtils.tla
@@ -10,3 +10,28 @@ IOSerialize(val, absoluteFilename, compress) == TRUE
IODeserialize(absoluteFilename, compressed) == CHOOSE val : TRUE
============================================================================
+
+\* Writes a TLA+ expression to the given file.
+\* TODO: Decide if it should write a module preamble.
+\* Write is easier to implement than Read because it goes from (valid) TLA+
+\* and writes a file whereas Read has to parse it.
+IOWrite(exp, absoluteFilename) == TRUE
+
+\* Reads a TLA+ expression from a file.
+\* TODO: Decide if it reads just a string from a file or expects (valid) TLA+.
+\* For trace validation it would be preferable to read strings.
+\* Constant-level expression (what's a use-case to read fileS during state space exploration?
+IORead(absoluteFilename) == TRUE
+
+TLAParse(IORead("C:\\foo\bar"))
+
+(* condition generally of level up to action (but not temporal), e.g. Inv'. *)
+
+\* Run the given OS command if condition evaluates to true.
+\* This is novel and cannot be done otherwise.
+\* TODO: What to pass to os command? osCommand a string that a user munges directly
+\* in/with TLA+.
+\* Can IOWrite/IORead be spec'ed with IOExec?
+IOExec(condition, osCommand) == TRUE
+
+=============================================================================