diff --git a/.github/workflows/mega-linter.yml b/.github/workflows/mega-linter.yml index f24067b7..017f182d 100644 --- a/.github/workflows/mega-linter.yml +++ b/.github/workflows/mega-linter.yml @@ -17,7 +17,7 @@ env: # Comment env block if you do not want to apply fixes #APPLY_FIXES_MODE: pull_request # If APPLY_FIXES is used, defines if the fixes are directly committed (commit) or posted in a PR (pull_request) DISABLE_LINTERS: SPELL_CSPELL,COPYPASTE_JSCPD,REPOSITORY_GITLEAKS,GROOVY_NPM_GROOVY_LINT FILTER_REGEX_EXCLUDE: .*/.*gradle - + concurrency: group: ${{ github.ref }}-${{ github.workflow }} cancel-in-progress: true @@ -31,7 +31,7 @@ jobs: - name: Checkout Code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 with: - token: ${{ secrets.GITHUB_TOKEN }} # secrets.PAT || + token: ${{ secrets.GITHUB_TOKEN }} # secrets.PAT || fetch-depth: 0 # If you use VALIDATE_ALL_CODEBASE = true, you can remove this line to improve performances # MegaLinter @@ -39,7 +39,7 @@ jobs: id: ml # You can override MegaLinter flavor used to have faster performances # More info at https://megalinter.github.io/flavors/ - uses: oxsecurity/megalinter/flavors/dotnet@v8.1.0 + uses: oxsecurity/megalinter/flavors/dotnet@v8.3.0 env: # All available variables are described in documentation # https://megalinter.github.io/configuration/ diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0e60e793..496e8055 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -25,49 +25,48 @@ jobs: runs-on: ${{ matrix.os }} steps: - # Git Checkout - - name: Checkout Code - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 + # Git Checkout + - name: Checkout Code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 - - name: Configure AWS Credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: arn:aws:iam::712023778557:role/github/GitHub-Testing-NF-Quilt - aws-region: us-east-1 + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::712023778557:role/github/GitHub-Testing-NF-Quilt + aws-region: us-east-1 - - name: Setup Java ${{matrix.java_version}} - uses: actions/setup-java@v4 - with: - java-version: ${{matrix.java_version}} - distribution: 'temurin' - architecture: x64 - cache: gradle - - - name: Setup Gradle - uses: gradle/actions/setup-gradle@v4 + - name: Setup Java ${{matrix.java_version}} + uses: actions/setup-java@v4 + with: + java-version: ${{matrix.java_version}} + distribution: "temurin" + architecture: x64 + cache: gradle - - name: Run Gradle Tests - run: make test - env: - LOG4J_DEBUG: true + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 - - name: Archive production artifacts (Windows only) - uses: actions/upload-artifact@v4 - if: ${{ always() && matrix.os == 'windows-latest' }} - with: - name: nf-quilt-test-reports-${{ matrix.os }}-${{ matrix.java_version }} - path: | + - name: Run Gradle Tests + run: make test + env: + LOG4J_DEBUG: true + + - name: Archive production artifacts (Windows only) + uses: actions/upload-artifact@v4 + if: ${{ always() && matrix.os == 'windows-latest' }} + with: + name: nf-quilt-test-reports-${{ matrix.os }}-${{ matrix.java_version }} + path: | D:\a\nf-quilt\nf-quilt\plugins\nf-quilt\build\reports\ - overwrite: true - - name: Archive production artifacts (Linux and MacOS) - uses: actions/upload-artifact@v4 - if: ${{ always() && matrix.os != 'windows-latest' }} - with: - name: nf-quilt-test-reports-${{ matrix.os }}-${{ matrix.java_version }} - path: | + overwrite: true + - name: Archive production artifacts (Linux and MacOS) + uses: actions/upload-artifact@v4 + if: ${{ always() && matrix.os != 'windows-latest' }} + with: + name: nf-quilt-test-reports-${{ matrix.os }}-${{ matrix.java_version }} + path: | ${{ github.workspace }}/plugins/nf-quilt/build/reports/ - overwrite: true - + overwrite: true diff --git a/.gitignore b/.gitignore index d2b24865..ea9f81ed 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,8 @@ work out results bin +/bin/ +plugins/nf-quilt/bin # Ignore Gradle GUI config gradle-app.setting @@ -32,3 +34,4 @@ params.yaml null # quilt+* # nextflow +.aider* diff --git a/.groovylintrc.json b/.groovylintrc.json index 536acf30..e4b536a6 100644 --- a/.groovylintrc.json +++ b/.groovylintrc.json @@ -1,92 +1,92 @@ { - "extends": "recommended", - "rules": { - "CatchException": { - "enabled": false - }, - "CatchThrowable": { - "enabled": false - }, - "ClassJavadoc": { - "enabled": false - }, - "ClosureAsLastMethodParameter": { - "enabled": false - }, - "DuplicateNumberLiteral": { - "enabled": false - }, - "DuplicateStringLiteral": { - "enabled": false - }, - "FieldTypeRequired": { - "enabled": false - }, - "ImplicitClosureParameter": { - "enabled": false - }, - "JUnitPublicNonTestMethod": { - "enabled": false - }, - "JUnitTestMethodWithoutAssert": { - "enabled": false - }, - "JavaIoPackageAccess": { - "enabled": false - }, - "JavadocEmptyFirstLine": { - "enabled": false - }, - "JavadocEmptyReturnTag": { - "enabled": false - }, - "JavadocMissingParamDescription": { - "enabled": false - }, - "JavadocMissingThrowsDescription": { - "enabled": false - }, - "MethodCount": { - "enabled": false - }, - "MethodParameterTypeRequired": { - "enabled": false - }, - "MethodSize": { - "enabled": false - }, - "NoDef": { - "enabled": false - }, - "PrintStackTrace": { - "enabled": false - }, - "PropertyName": { - "enabled": false - }, - "SpaceAroundMapEntryColon": { - "enabled": false - }, - "SpaceAroundOperator": { - "enabled": false - }, - "SystemExit": { - "enabled": false - }, - "UnnecessaryGetter": { - "enabled": false - }, - "UnnecessaryObjectReferences": { - "enabled": false - }, - "UnnecessarySetter": { - "enabled": false - }, - "VariableName": { - "enabled": false - }, - "VariableTypeRequired": { - "enabled": false - } + "extends": "recommended", + "rules": { + "CatchException": { + "enabled": false + }, + "CatchThrowable": { + "enabled": false + }, + "ClassJavadoc": { + "enabled": false + }, + "ClosureAsLastMethodParameter": { + "enabled": false + }, + "DuplicateNumberLiteral": { + "enabled": false + }, + "DuplicateStringLiteral": { + "enabled": false + }, + "FieldTypeRequired": { + "enabled": false + }, + "ImplicitClosureParameter": { + "enabled": false + }, + "JUnitPublicNonTestMethod": { + "enabled": false + }, + "JUnitTestMethodWithoutAssert": { + "enabled": false + }, + "JavaIoPackageAccess": { + "enabled": false + }, + "JavadocEmptyFirstLine": { + "enabled": false + }, + "JavadocEmptyReturnTag": { + "enabled": false + }, + "JavadocMissingParamDescription": { + "enabled": false + }, + "JavadocMissingThrowsDescription": { + "enabled": false + }, + "MethodCount": { + "enabled": false + }, + "MethodParameterTypeRequired": { + "enabled": false + }, + "MethodSize": { + "enabled": false + }, + "NoDef": { + "enabled": false + }, + "PrintStackTrace": { + "enabled": false + }, + "PropertyName": { + "enabled": false + }, + "SpaceAroundMapEntryColon": { + "enabled": false + }, + "SpaceAroundOperator": { + "enabled": false + }, + "SystemExit": { + "enabled": false + }, + "UnnecessaryGetter": { + "enabled": false + }, + "UnnecessaryObjectReferences": { + "enabled": false + }, + "UnnecessarySetter": { + "enabled": false + }, + "VariableName": { + "enabled": false + }, + "VariableTypeRequired": { + "enabled": false } -} \ No newline at end of file + } +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 78b61df5..75441df0 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,4 @@ { - "java.compile.nullAnalysis.mode": "disabled", - "java.configuration.updateBuildConfiguration": "disabled" -} \ No newline at end of file + "java.compile.nullAnalysis.mode": "disabled", + "java.configuration.updateBuildConfiguration": "disabled" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fca9e5c..2bcf6393 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,9 +8,16 @@ - Improve handling of dynamically-specified URIs - Rewrite README.md, splitting out developer documentation to README_DEV.md +## [0.8.12] 2024-12-03 UNPUBLISHED + +- Extract flags and metadata from `nextflow.config` +- Remove unused methods and keys +- Remove support for propertyName +- Use IntelliJ to fix types and lint + ## [0.8.11] 2024-11-5 UNPUBLISHED -- Catch *all* toJson errors +- Catch _all_ toJson errors ## [0.8.10] 2024-11-4 UNPUBLISHED @@ -27,8 +34,8 @@ ## [0.8.7] 2024-10-23 UNPUBLISHED -- Use package cache instead of `params` to find output URIs - (in order to support dynamic URIs set by, e.g. `main.nf`) +- Use package cache instead of `params` to find output URIs (in order to support + dynamic URIs set by, e.g. `main.nf`) - Allow setting metadata from inside the workflow ## [0.8.6] 2024-09-11 @@ -52,7 +59,8 @@ ## [0.8.2] 2024-09-07 -- Use copyFile rather than writeString for overlay files [requires NextFlow 23 or later] +- Use copyFile rather than writeString for overlay files [requires NextFlow 23 + or later] - Restore README and quilt_summarize to output ## [0.8.1] 2024-09-05 @@ -198,7 +206,8 @@ Beta release (not yet on nextflow-io/plugins) - Use `msg` fragment parameter as commit message when writing packages - Removed Benchling support (will add back in a future release) -- Don't crash when writing to Quilt+ URIs with `&path=` fragments (by ignoring that part) +- Don't crash when writing to Quilt+ URIs with `&path=` fragments (by ignoring + that part) ## [0.3.5] 2023-04-05 @@ -219,5 +228,6 @@ Beta release (not yet on nextflow-io/plugins) ## [0.3.2] 2023-02-24 -- First official release on [nextflow-io/plugins](https://github.com/nextflow-io/plugins/commits/main/plugins.json) +- First official release on + [nextflow-io/plugins](https://github.com/nextflow-io/plugins/commits/main/plugins.json) - Read and write from Quilt+ URIs diff --git a/README-DEV.md b/README-DEV.md index 33058904..5ccf2969 100644 --- a/README-DEV.md +++ b/README-DEV.md @@ -2,10 +2,14 @@ ## Using Pre-Release Versions -Occasionally we will release beta versions of the plugin that are not yet available in the Nextflow plugin registry. You can help test these versions as follows: +Occasionally we will release beta versions of the plugin that are not yet +available in the Nextflow plugin registry. You can help test these versions as +follows: -- Set the `NXF_PLUGINS_TEST_REPOSITORY` environment variable to the URL of the plugin's metadata file -- Specify the plugin version in the `plugins` section of your `nextflow.config` file +- Set the `NXF_PLUGINS_TEST_REPOSITORY` environment variable to the URL of the + plugin's metadata file +- Specify the plugin version in the `plugins` section of your `nextflow.config` + file From the command-line, do, e.g.: @@ -51,7 +55,7 @@ To quickly run `nf-quilt` from this GitHub repository: # install and compiles dependencies, then test make test-all # create "test/hurdat" package on s3://$WRITE_BUCKET -make pkg-test WRITE_BUCKET=your-writeablebucket +make pkg-test WRITE_BUCKET=your-writeablebucket ``` This ensures you have properly installed Nextflow and configured your local @@ -77,7 +81,7 @@ file (be sure to rename the `outdir` parameter if you use different convention). For example: ```bash - ./launch.sh run ./main.nf -profile standard -plugins $(PROJECT) --outdir "quilt+s3://bucket#package=test/hurdat" +./launch.sh run ./main.nf -profile standard -plugins $(PROJECT) --outdir "quilt+s3://bucket#package=test/hurdat" ``` ### Unit Testing diff --git a/README.md b/README.md index e53c4e30..35905f60 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,17 @@ Nextflow plugin for reading and writing Quilt packages [`nf-quilt`](https://github.com/quiltdata/nf-quilt) is a Nextflow -[plugin](https://www.nextflow.io/docs/latest/plugins.html) developed by [Quilt -Data, Inc.](https://quiltdata.com/) that enables you read and write directly to -Quilt packages wherever your Nextflow pipeline currently uses `s3` URIs. It -works with any Amazon S3-compatible object store, as long as you have the -appropriate +[plugin](https://www.nextflow.io/docs/latest/plugins.html) developed by +[Quilt Data, Inc.](https://quiltdata.com/) that enables you read and write +directly to Quilt packages wherever your Nextflow pipeline currently uses `s3` +URIs. It works with any Amazon S3-compatible object store, as long as you have +the appropriate [credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html). Quilt packages are versioned, immutable, and shareable data containers that store data, metadata, and documentation as a single atomic unit. They are -accessible even by non-technical users via the Quilt Platfrom, a graphical -web catalog which runs either in your private AWS cloud or on +accessible even by non-technical users via the Quilt Platfrom, a graphical web +catalog which runs either in your private AWS cloud or on [open.quiltdata.com](https://open.quiltdata.com). ## Writing to Quilt Packages @@ -36,7 +36,8 @@ metadata from that run. ## Use nextflow.config to configure the plugin -To avoid having to manually specify the plugin, you can add it to your `nextflow.config` file: +To avoid having to manually specify the plugin, you can add it to your +`nextflow.config` file: ```groovy plugins { @@ -49,7 +50,8 @@ This also works with Nextflow Tower, where you can add the plugin to the ![Example Tower Configuration](./images/tower-config.png) -As of v0.9+, you can also add a `quilt` section to your `nextflow.config` file to specify the metadata and other plugin behaviors: +As of v0.9, you can also add a `quilt` section to your `nextflow.config` file to +specify the metadata and other plugin behaviors: ```groovy quilt { @@ -73,19 +75,23 @@ quilt+s3://$WRITE_BUCKET#package=nf_quilt/rnaseq ### Input URIs -Versioned Quilt+ URIs can be used as input URIs in your Nextflow pipeline, to ensure you know precisely which data you are using. For example, this is a specific version of the output from `nf-core/sarek`: +Versioned Quilt+ URIs can be used as input URIs in your Nextflow pipeline, to +ensure you know precisely which data you are using. For example, this is a +specific version of the output from `nf-core/sarek`: ```shell quilt+s3://$READ_BUCKET#package=nf-core/sarek@8a2164f48be8e0d6385f64b76b74a8543e9fb1b12a8eff6daeaffa653d52fcf7 ``` -If are using the Quilt Platform, you can find the Quilt+ URI for a package in the `<> CODE | URI` section at the top of the package page. +If are using the Quilt Platform, you can find the Quilt+ URI for a package in +the `<> CODE | URI` section at the top of the package page. ![Example Quilt+ URI](./images/quilt-uri.png) ### Output URIs -You can specify a Quilt+ URI as the `--outdir` parameter in your Nextflow pipeline if you want to specify the package name and metadata for the output, +You can specify a Quilt+ URI as the `--outdir` parameter in your Nextflow +pipeline if you want to specify the package name and metadata for the output, but don't care about the precise location in S3. For example: ```shell @@ -94,52 +100,66 @@ nextflow run nf-core/rnaseq --outdir "quilt+s3://$WRITE_BUCKET?key=value#package The `key=value` part is optional, and can be used to specify metadata for the package, which will be added to the metadata automatically generated by the -plugin. This is particularly when working with [Quilt -workflows](https://docs.quiltdata.com/workflows), which require specific +plugin. This is particularly when working with +[Quilt workflows](https://docs.quiltdata.com/workflows), which require specific metadata to be present before a package can be created. ### CLI Usage -If your workflow supports `--input` and `--outdir` parameters, you can use them to specify the Quilt+ URIs. For example: +If your workflow supports `--input` and `--outdir` parameters, you can use them +to specify the Quilt+ URIs. For example: ```shell nextflow run main.nf --input "quilt+s3://$READ_BUCKET#package=nf-core/sarek@8a2164f48be8e0d6385f64b76b74a8543e9fb1b12a8eff6daeaffa653d52fcf7" --outdir "quilt+s3://$WRITE_BUCKET?key=value#package=test/my-sarek-processor" ``` -Note that you need to quote the URIs to prevent the shell from interpreting the `?` and `#` characters. +Note that you need to quote the URIs to prevent the shell from interpreting the +`?` and `#` characters. ## Configurations There are a number of additional parameters you can set in order to customize the behavior of the plugin: -* **catalog**: specify the DNS hostname of the Quilt catalog to use (default: `open.quiltdata.com`) -* **force**: completely replace the existing package, rather than updating it (default: `false`) -* **meta**: specify a map of metadata to add to the package (default: `{}`) -* **msg**: specify the commit message template to use when saving the package -* **pkg**: specify the name of the package to read or write, when using an S3 URI (default: the first two path components) -* **readme**: specify a template string for the package README_NF_QUILT.md file -* **workflow**: specify the name of a Quilt workflow on that bucket to use for metadata validation (default: None) +- **catalog**: specify the DNS hostname of the Quilt catalog to use (default: + None) +- **force**: completely replace the existing package, rather than updating it + (default: `false`) +- **meta**: specify a map of metadata to add to the package (default: `{}`) +- **message**: specify the commit message template to use when saving the + package +- **package**: specify the name of the package to read or write, when using an + S3 URI (default: the first two path components) +- **readme**: specify a template string for the package README_NF_QUILT.md file +- **summarize**: which files to display on the "front page" of the package via + `quilt_summarize.json` (default: + `*.md,*.html,*.?sv,*.pdf,igv.json,**/multiqc_report.html`); use `false` to + disable. +- **workflow**: specify the name of a Quilt workflow on that bucket to use for + metadata validation (default: None) NOTE: These configurations were previously specified as part of the Quilt+ URI. -That functionality has been deprecated, and may be removed in a future release. +That functionality has mostly been removed. The Quilt+ URI fragment (`#`) is now +only used to specify the package and (optionally) the path and workflow. You may +continue to use the query string (`?`) to specify metadata, including the +`catalog`. ### Template Strings -Version 0.3.4 and later allow you to customize both the `msg` -and `readme` via template strings that use these `${variables}`: +You can customize both the `msg` and `readme` via template strings that use +these `${variables}`: -* `cmd`: the current command line -* `meta`: the complete metadata -* `msg`: the current commit message -* `nextflow`: the current Nextflow configuration -* `now`: the ISO 8601 date and time -* `pkg`: the package name +- `cmd`: the current command line +- `meta`: the complete metadata +- `msg`: the current commit message +- `nextflow`: the current Nextflow configuration +- `now`: the ISO 8601 date and time +- `pkg`: the package name Note that the full `meta` is usually extremely large. You should use conditional keys to extract only the metadata you need, if present. For example: -```groovy +````groovy quilt { meta = [pipeline: 'nf-core/rnaseq'] msg = "${meta['config']?.get('runName')}: ${meta['cmd']}" @@ -169,4 +189,4 @@ ${nextflow} `${meta['workflow']?.get('stats')?.getAt('processes')}` ''' } -``` +```` diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 94113f20..e2847c82 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/nextflow.config b/nextflow.config index e128c083..bc2e527a 100644 --- a/nextflow.config +++ b/nextflow.config @@ -3,7 +3,7 @@ plugins { } quilt { - catalog = 'open.quiltdata.com' - meta = [pipeline: 'nf-core/rnaseq'] + catalog = 'nightly.quilttest.com' + meta = [pipeline: 'nf-core/rnaseq', key4: 2+2] force = false } diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserverFactory.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserverFactory.groovy index a475ad4f..92db5024 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserverFactory.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltObserverFactory.groovy @@ -33,7 +33,10 @@ class QuiltObserverFactory implements TraceObserverFactory { @Override Collection create(Session session) { //log.debug("`create` ${this}") - return (Collection) [new QuiltObserver()] + Collection quiltObservers = new ArrayList<>() + quiltObservers.add(new QuiltObserver()) + + return quiltObservers as Collection } } diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltPathify.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltPathify.groovy index b07f5182..6523e71c 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltPathify.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltPathify.groovy @@ -51,7 +51,7 @@ class QuiltPathify { Files.copy(source, dest) } catch (Exception e) { - log.error("writeString: cannot write `$source` to `$dest` in `${destRoot}`") + log.error("writeString: cannot write `$source` to `$dest` in `${destRoot}`\n$e") } } diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltProduct.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltProduct.groovy index 392337e2..5240bbe1 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/QuiltProduct.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/QuiltProduct.groovy @@ -16,6 +16,7 @@ package nextflow.quilt import nextflow.quilt.jep.QuiltPackage +import nextflow.quilt.jep.QuiltParser import nextflow.quilt.nio.QuiltPath import nextflow.Session @@ -31,8 +32,8 @@ import java.nio.file.SimpleFileVisitor import java.time.LocalDateTime import groovy.transform.CompileStatic -import groovy.util.logging.Slf4j import groovy.text.GStringTemplateEngine +import groovy.util.logging.Slf4j import groovy.json.JsonOutput /** @@ -47,12 +48,18 @@ class QuiltProduct { public final static String README_FILE = 'README_NF_QUILT.md' public final static String SUMMARY_FILE = 'quilt_summarize.json' - private final static String KEY_META = 'metadata' - private final static String KEY_README = 'readme' - private final static String KEY_SKIP = 'SKIP' - private final static String KEY_SUMMARIZE = 'summarize' + public final static String KEY_META = 'meta' + public final static String KEY_MSG = 'message' + public final static String KEY_QUILT = 'quilt' + public final static String KEY_README = 'readme' + public final static String KEY_SUMMARIZE = 'summarize' + +/* groovylint-disable-next-line GStringExpressionWithinString */ + private final static String DEFAULT_MSG = ''' +${config.get('runName')}: ${meta.get('cmd')} +''' - /* groovylint-disable-next-line GStringExpressionWithinString */ +/* groovylint-disable-next-line GStringExpressionWithinString */ private final static String DEFAULT_README = ''' # ${pkg} @@ -80,6 +87,7 @@ ${nextflow} `${meta['workflow']?.get('stats')?.getAt('processes')}` ''' + private final static String DEFAULT_SUMMARIZE = '*.md,*.html,*.?sv,*.pdf,igv.json,**/multiqc_report.html' private final static String[] BIG_KEYS = [ @@ -88,12 +96,17 @@ ${nextflow} ] static void printMap(Map map, String title) { - log.info("\n\n\n# $title") + log.info("\n\n\n# $title ${map.keySet()}") map.each { key, value -> log.info("\n## ${key}: ${value}") } } + static Map extractMap(Map map, String key) { + def child = map.remove(key) + return (child instanceof Map) ? child : [:] + } + static void writeString(String text, QuiltPackage pkg, String filepath) { String dir = pkg.packageDest() Path path = Paths.get(dir, filepath.split('/') as String[]) @@ -102,7 +115,7 @@ ${nextflow} Files.write(path, text.bytes) } catch (Exception e) { - log.error("writeString: cannot write `$text` to `$path` for `${pkg}`") + log.error("writeString: cannot write `$text` to `$path` for `${pkg}`\n$e") } } @@ -113,7 +126,7 @@ ${nextflow} Files.copy(source, dest) } catch (Exception e) { - log.error("writeString: cannot write `$source` to `$dest` in `${destRoot}`") + log.error("writeString: cannot write `$source` to `$dest` in `${destRoot}`\n$e.message()") } } @@ -122,124 +135,75 @@ ${nextflow} return time.toString().replace(':', '-').replace('T', 't') } - private final QuiltPath path - private final QuiltPackage pkg - private final Session session - private String msg - private Map meta + protected final QuiltPath path + protected final QuiltPackage pkg + protected final Session session + protected final Map> config + + protected final Map metadata + protected final Expando flags = new Expando([ + catalog: false, + force: false, + message: DEFAULT_MSG, + meta: true, + readme: DEFAULT_README, + summarize: DEFAULT_SUMMARIZE, + workflow: false, + ]) QuiltProduct(QuiltPathify pathify, Session session) { + println("Creating QuiltProduct: ${pathify}") + this.session = session + this.config = session.config ?: [:] this.path = pathify.path + println('QuiltProduct.path') this.pkg = pathify.pkg - this.msg = pkg.toString() - this.meta = pkg.meta + [pkg: msg, time_start: now()] - this.session = session - println("QuiltProduct: ${pkg.toUriString()}") - println("\tQuiltProduct.pkg: ${pkg}") - println("\tQuiltProduct.path: ${path}") - - if (session.isSuccess() || pkg.is_force()) { + println('QuiltProduct.pkg') + this.metadata = collectMetadata() + println('QuiltProduct.metadata') + // println("QuiltProduct.flags: ${flags}") + if (session.isSuccess() || flags.getProperty(QuiltParser.P_FORCE) == true) { publish() } else { log.info("not publishing: ${pkg} [unsuccessful session]") } } - void publish() { - log.debug("publish($msg)") - addSessionMeta() - setupReadme() - setupSummarize() - try { - log.info("publish.pushing: ${pkg}") - def m = pkg.push(msg, meta) - log.info("publish.pushed: ${m}") - } - catch (Exception e) { - log.error("Exception: ${e}") - print("FAILED: $pkg\n") - e.printStackTrace() - /* groovylint-disable-next-line ThrowRuntimeException */ - throw new RuntimeException(e) - } - print("SUCCESS: $pkg\n") - } - - boolean shouldSkip(key) { - return pkg.meta.containsKey(key) && pkg.meta[key] == KEY_SKIP - } - - boolean addSessionMeta() { - println("addSessionMeta: ${session}") + Map collectMetadata() { if (shouldSkip(KEY_META)) { - return false - } - - Map> cf = session.config - println("addSessionMeta.cf: ${cf}") - if (cf == null) { - log.error('addSessionMeta: no config found', pkg.meta) - return false + log.info("SKIP: metadata for ${pkg}") + return [:] } - Map qf = cf.navigate('quilt') as Map ?: [:] - qf['package_id'] = pkg.toString() - qf['uri'] = path.toUriString() - println("addSessionMeta.qf: ${qf}") - Map cmeta = qf.navigate('meta') as Map - qf.remove('meta') - println("addSessionMeta.cmeta: ${cmeta}") - - try { - Map smeta = getMetadata(cf) - // println("addSessionMeta.smeta: ${smeta}") - smeta['quilt'] = qf - smeta.remove('config') - meta += smeta + cmeta - msg = "${cf.get('runName')}: ${meta['cmd']}" - } catch (Exception e) { - println("addSessionMeta.getMetadata failed: $e") - log.error("addSessionMeta.getMetadata failed: ${e.getMessage()}", pkg.meta) - return false - } - writeNextflowMetadata(meta, 'metadata') - return true - } - - String writeNextflowMetadata(Map map, String suffix) { - String filename = "nf-quilt/${suffix}.json" - log.debug("writeNextflowMetadata[$suffix]: ${filename}") - try { - writeString(QuiltPackage.toJson(map), pkg, filename) - } catch (Exception e) { - log.error("writeNextflowMetadata.toJson failed: ${e.getMessage()}", map) - } - return filename - } - - Map getMetadata(Map cf) { - // add metadata from quilt and URI - if (cf != null) { - cf.remove('executor') - cf.remove('params') - cf.remove('session') - writeNextflowMetadata(cf, 'config') - cf.remove('process') - printMap(cf, 'config') - } - Map params = session.getParams() + println("collectMetadata.config: ${config}") + config.remove('executor') + config.remove('params') + config.remove('session') + writeMapToPackage(config, 'config') + config.remove('process') + printMap(config, 'config') + + Map quilt_cf = extractMap(config, KEY_QUILT) + println("collectMetadata.quilt_cf: ${quilt_cf}") + Map pkg_meta = pkg.getMetadata() + println("collectMetadata.pkg_meta: ${pkg_meta}") + updateFlags(pkg_meta, quilt_cf) + + Map params = session.getParams() + println("collectMetadata.params: ${params}") if (params != null) { - writeNextflowMetadata(params, 'params') + writeMapToPackage(params, 'params') params.remove('genomes') params.remove('test_data') printMap(params, 'params') } - Map wf = session.getWorkflowMetadata().toMap() - String start = wf['start'] - String complete = wf['complete'] - String cmd = wf['commandLine'] + Map wf = session.getWorkflowMetadata()?.toMap() + println("collectMetadata.wf: ${wf}") + String start = wf?.get('start') + String complete = wf?.get('complete') + String cmd = wf?.get('commandLine') if (wf != null) { BIG_KEYS.each { k -> wf[k] = "${wf[k]}" } - writeNextflowMetadata(wf, 'workflow') + writeMapToPackage(wf, 'workflow') wf.remove('container') wf.remove('start') wf.remove('complete') @@ -249,56 +213,154 @@ ${nextflow} log.info("\npublishing: ${wf['runName']}") } - return [ + Map cf_meta = extractMap(quilt_cf, KEY_META) // remove after setting flags + println("getMetadata.cf_meta: ${cf_meta}") + Map base_meta = cf_meta + pkg_meta + log.info("getMetadata.base_meta: ${base_meta}") + return base_meta + [ cmd: cmd, - config: cf, + now: now(), params: params, + quilt: quilt_cf, time_start: start, time_complete: complete, + uri: path.toUriString(), workflow: wf, ] } - String setupReadme() { - String text = 'Stub README' + Map getTemplateArgs() { + Map params = [ + cmd: metadata.get('cmd'), + config: config, + meta: metadata, + now: metadata.get('now'), + pkg: flags.getProperty(QuiltParser.P_PKG) + ] + return params + } + + boolean shouldSkip(String key) { + return flags.getProperty(key) == false + } + + /** + * Update flags from default values + * Use metadata if available, otherwise use config + * + * @param meta Map of package metadata + * @param cf Map of config (from nextflow.config)` + */ + void updateFlags(Map pkg_meta, Map cf_meta) { + println("updateFlags.pkg_meta: ${pkg_meta}") + println("updateFlags.cf_meta: ${cf_meta}") + for (String key : flags.getProperties().keySet()) { + if (pkg_meta.containsKey(key)) { + flags.setProperty(key, pkg_meta[key]) + } else if (cf_meta.containsKey(key)) { + flags.setProperty(key, cf_meta[key]) + } + } + // TODO: should this only work for names inferred from S3 URIs? + String pkgName = cf_meta.containsKey(QuiltParser.P_PKG) ? cf_meta[QuiltParser.P_PKG] : pkg.packageName + flags.setProperty(QuiltParser.P_PKG, pkgName) + } + + String writeMapToPackage(Map map, String prefix) { + String filename = "nf-quilt/${prefix}.json" + log.debug("writeMapToPackage[$prefix]: ${filename}") try { - text = makeReadme() + writeString(QuiltPackage.toJson(map), pkg, filename) + } catch (Exception e) { + log.error("writeMapToPackage.toJson failed: ${e.getMessage()}", map) + } + return filename + } + +/* + * Publish the package to the Quilt catalog + */ + + void publish() { + log.info("publish.pushing: ${pkg}") + try { + String message = compileMessage() + writeReadme(message) + writeSummarize() + def rc = pkg.push(message, metadata) + log.info("publish.pushed: ${rc}") } catch (Exception e) { - log.error("setupReadme failed: ${e.getMessage()}\n{$e}", pkg.meta) + log.error("Exception: ${e}") + print("publish.FAILED: $pkg\n") + e.printStackTrace() + return } - if (text != null && text.length() > 0) { - log.debug("setupReadme: ${text.length()} bytes") - writeString(text, pkg, README_FILE) + print("\nSUCCESS: ${displayName()}\n") + } + + String displayName() { + Object catalog = flags.getProperty(QuiltParser.P_CAT) + return catalog ? pkg.toCatalogURL(catalog.toString()) : pkg.toUriString() + } + + String compileMessage() { + String msg = flags.getProperty(KEY_MSG) + GStringTemplateEngine engine = new GStringTemplateEngine() + println("compileMessage: ${msg}") + try { + String output = engine.createTemplate(msg).make(getTemplateArgs()) + log.debug("compileMessage.output: ${output}") + return output } - return text + catch (Exception e) { + log.error("compileMessage failed: ${e.getMessage()}\n{$e}", flags) + } + return "compileMessage.FAILED\n$msg" } - String makeReadme() { + String compileReadme(String msg) { if (shouldSkip(KEY_README)) { - log.info("readme=SKIP for ${pkg}") + log.info("SKIP: readme for ${pkg}") return null } - GStringTemplateEngine engine = new GStringTemplateEngine() - String raw_readme = pkg.meta_overrides(KEY_README, DEFAULT_README) - String cmd = "${meta['cmd']}".replace(' -', ' \\\n -') - String nf = meta['workflow']?['nextflow'] + String raw_readme = flags.getProperty(KEY_README) + String nf = metadata['workflow']?['nextflow'] String nextflow = nf?.replace(', ', '```\n - **')\ ?.replace('nextflow.NextflowMeta(', ' - **')\ ?.replace(')', '```') ?.replace(':', '**: ```') - Map params = [ - cmd: cmd, - meta: meta, + Map params = getTemplateArgs() + params += [ msg: msg, nextflow: nextflow, - now: now(), - pkg: pkg.packageName, ] - log.debug("makeReadme.params: ${params}") - String template = engine.createTemplate(raw_readme).make(params) - log.debug("makeReadme.template: ${template}") - return template + log.debug("compileReadme.params: ${params}") + try { + GStringTemplateEngine engine = new GStringTemplateEngine() + String output = engine.createTemplate(raw_readme).make(params) + log.debug("compileReadme.output: ${output}") + return output + } + catch (Exception e) { + log.error("compileReadme failed: ${e.getMessage()}\n{$e}", flags) + } + return "compileReadme.FAILED\n$raw_readme" + } + + String writeReadme(String message) { + String text = 'Stub README' + try { + text = compileReadme(message) + } + catch (Exception e) { + log.error("writeReadme failed: ${e.getMessage()}\n{$e}", flags) + } + if (text != null && text.length() > 0) { + log.debug("writeReadme: ${text.length()} bytes") + writeString(text, pkg, README_FILE) + } + return text } List match(String glob) throws IOException { @@ -330,15 +392,17 @@ ${nextflow} return matches } - List setupSummarize() { + List writeSummarize() { List quilt_summarize = [] if (shouldSkip(KEY_SUMMARIZE)) { + log.info("SKIP: summarize for ${flags}") return quilt_summarize } - String summarize = pkg.meta_overrides(KEY_SUMMARIZE, DEFAULT_SUMMARIZE) + String summarize = flags.getProperty(KEY_SUMMARIZE) String[] wildcards = summarize.split(',') wildcards.each { wildcard -> List paths = match(wildcard) + println("writeSummarize: ${paths.size()} matches for ${wildcard}") paths.each { path -> String filename = path.getFileName() Map entry = ['path': path.toString(), 'title': filename] @@ -351,7 +415,7 @@ ${nextflow} writeString(qs_json, pkg, SUMMARY_FILE) } catch (Exception e) { - log.error("setupSummarize.toJson failed: ${e.getMessage()}\n{$e}", SUMMARY_FILE) + log.error("writeSummarize.toJson failed: ${e.getMessage()}\n{$e}", SUMMARY_FILE) } return quilt_summarize } diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltID.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltID.groovy index 28d9691a..19c867fb 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltID.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltID.groovy @@ -15,9 +15,6 @@ */ package nextflow.quilt.jep -/* groovylint-disable-next-line ImportFromSamePackage */ -import nextflow.quilt.jep.QuiltParser - import groovy.transform.CompileStatic import groovy.util.logging.Slf4j diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltPackage.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltPackage.groovy index 60ea4f7d..17894f50 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltPackage.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltPackage.groovy @@ -22,6 +22,7 @@ import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import java.nio.file.FileSystems import java.nio.file.Files +import java.nio.file.NoSuchFileException import java.nio.file.Path import java.nio.file.Paths import java.nio.file.SimpleFileVisitor @@ -48,8 +49,8 @@ class QuiltPackage { private static final String INSTALL_PREFIX = 'QuiltPackage' static final Path INSTALL_ROOT = Files.createTempDirectory(INSTALL_PREFIX) + public final String packageName private final String bucket - private final String packageName private final QuiltParser parsed private final String hash private final Path folder @@ -60,10 +61,6 @@ class QuiltPackage { return FileSystems.getDefault().getSeparator() } - static String osJoin(String... parts) { - return parts.join(osSep()) - } - static String osConvert(String path) { return path.replace('/', FileSystems.getDefault().getSeparator()) } @@ -97,11 +94,6 @@ class QuiltPackage { PKGS.clear() } - static QuiltPackage forUriString(String uri) { - QuiltParser parsed = QuiltParser.forUriString(uri) - return forParsed(parsed) - } - static QuiltPackage forParsed(QuiltParser parsed) { println("QuiltPackage.forParsed: $parsed") boolean isNull = parsed.hasNullBucket() @@ -119,17 +111,6 @@ class QuiltPackage { return pkg } - static boolean hasKey(String pkgKey) { - return PKGS.containsKey(pkgKey) - } - - static QuiltPackage forKey(String pkgKey) { - if (hasKey(pkgKey)) { - return PKGS.get(pkgKey) - } - return null - } - static List listDirectory(Path rootPath) { return Files.walk(rootPath).sorted(Comparator.reverseOrder()).collect(Collectors.toList()) } @@ -147,8 +128,8 @@ class QuiltPackage { Files.deleteIfExists(path) } } - catch (java.nio.file.NoSuchFileException e) { - log.debug 'deleteDirectory: ignore non-existent files' + catch (NoSuchFileException e) { + log.debug "deleteDirectory: ignore non-existent files\n$e" } return true } @@ -205,7 +186,7 @@ class QuiltPackage { } boolean is_force() { - return parsed.options[QuiltParser.P_FORCE] + return parsed.getOptions(QuiltParser.P_FORCE) } boolean isNull() { @@ -220,7 +201,7 @@ class QuiltPackage { S3PhysicalKey key = new S3PhysicalKey(bucket, '', null) try { key.listRecursively() - } catch (Exception e) { + } catch (IOException e) { log.error("isBucketAccessible: failed to check $bucket", e) return false } @@ -231,6 +212,10 @@ class QuiltPackage { return folder } + Map getMetadata() { + return this.meta + } + /* * Package methods */ @@ -315,7 +300,7 @@ class QuiltPackage { LocalPhysicalKey physicalKey = new LocalPhysicalKey(f) long size = Files.size(f) builder.addEntry(logicalKey, new Entry(physicalKey, size, null, null)) - }); + }) Map fullMeta = [ 'version': Manifest.VERSION, @@ -337,7 +322,7 @@ class QuiltPackage { /* groovylint-disable-next-line ThrowRuntimeException */ throw new RuntimeException(e) } - return m + // return m } @Override @@ -349,13 +334,12 @@ class QuiltPackage { return parsed.toUriString() } - String toKey() { - return parsed.toPackageString(true) + String toCatalogURL(String catalog) { + return "https://${catalog}/b/${bucket}/packages/${packageName}" } - String meta_overrides(String key, Serializable baseline = null) { - Object temp = meta[key] ? meta[key] : baseline - return temp.toString() + String toKey() { + return parsed.toPackageString(true) } } diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltParser.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltParser.groovy index a9123569..b0ee2e81 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltParser.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/jep/QuiltParser.groovy @@ -30,33 +30,24 @@ class QuiltParser { static final int MIN_SIZE = 2 static final String NULL_BUCKET = 'nf-quilt-dev-null' - static final String P_CAT = 'catalog' - static final String P_DEST = 'dest' - static final String P_DREG = 'dest-registry' - static final String P_DIR = 'dir' - static final String P_FORCE = 'force' - static final String P_HASH = 'top-hash' - static final String P_PATH = 'path' - static final String P_PKG = 'package' - static final String P_PROP = 'property' - static final String P_REG = 'registry' - static final String P_WORK = 'workflow' - static final String[] INSTALL_KEYS = [P_REG, P_DEST, P_DREG, P_HASH, P_PATH] - static final String[] PUSH_KEYS = [P_REG, P_DIR, P_WORK, P_FORCE] + public static final String P_CAT = 'catalog' + public static final String P_FORCE = 'force' + public static final String P_PATH = 'path' + public static final String P_PKG = 'package' + public static final String P_WORK = 'workflow' private final String bucket private final String packageName - private final String propertyName private final String workflowName private final String catalogName private String[] paths private String hash private String tag - private final Map options - private final Map metadata + protected final Map options + protected final Map metadata static QuiltParser forBarePath(String path) { - return QuiltParser.forUriString(PREFIX + path) + return forUriString(PREFIX + path) } static QuiltParser forNullBucket() { @@ -65,7 +56,7 @@ class QuiltParser { static QuiltParser forUriString(String uriString) { URI uri = new URI(uriString) - return QuiltParser.forURI(uri) + return forURI(uri) } static QuiltParser forURI(URI uri) { @@ -92,37 +83,38 @@ class QuiltParser { static Map parseQuery(String query) { if (!query) { return [:] } // skip for urls without query params def params = query.split('&') - def result = [:] + Map result = [:] params.each { param -> def keyValue = param.split('=') if (keyValue.size() == 2) { String key = decode(keyValue[0]) String value = decode(keyValue[1]) if (result.containsKey(key)) { - if (result[key] instanceof List) { - result[key].add(value) + Object listVal = result[key] + if (listVal instanceof List) { + listVal << value } else { - result[key] = [result[key], value] + result[key] = [listVal, value] } } else { result[key] = value } } } - return result + return result as Map } static String encodePair(String key, String value) { - return "${QuiltParser.encode(key)}=${QuiltParser.encode(value)}" + return "${encode(key)}=${encode(value)}" } static String unparseQuery(Map query) { if (!query) { return '' } // skip for urls without query params List params = query.collect { key, value -> if (value instanceof List) { - value.collect { QuiltParser.encodePair(key, it.toString()) }.join('&') + value.collect { encodePair(key, it.toString()) }.join('&') } else { - QuiltParser.encodePair(key, value.toString()) + encodePair(key, value.toString()) } } return params.join('&') @@ -138,12 +130,11 @@ class QuiltParser { this.bucket = bucket this.paths = path ? path.split(SEP) : [] as String[] this.packageName = parsePkg(pkg) - this.propertyName = options.get(P_PROP) this.workflowName = options.get(P_WORK) this.catalogName = options.get(P_CAT) this.options = options this.metadata = metadata - // log.debug("QuiltParser[${bucket}] for ${packageName} in ${path}") + // log.debug("QuiltParser[${bucket}] for ${packageName} in ${path}") } String parsePkg(String pkg) { @@ -232,10 +223,6 @@ class QuiltParser { return packageName } - String getPropertyName() { - return propertyName - } - String getWorkflowName() { return workflowName } @@ -278,7 +265,7 @@ class QuiltParser { } String getOptions(String key) { - return options?.get(key) + return options?.get(key) as String } String toPackageString(boolean forKey = false) { @@ -300,9 +287,6 @@ class QuiltParser { if (!hasPath()) { return str } str += (packageName) ? '&' : '#' str += "path=${getPath().replace('/', '%2f')}" - if (propertyName) { - str += "&property=${propertyName}" - } if (workflowName) { str += "&workflow=${workflowName}" } diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileAttributesView.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileAttributesView.groovy index b51abd42..1b8b4349 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileAttributesView.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileAttributesView.groovy @@ -49,7 +49,7 @@ class QuiltFileAttributesView implements BasicFileAttributeView { /** * This API is implemented is not supported but instead of throwing an exception just do nothing - * to not break the method {@link java.nio.file.CopyMoveHelper#copyToForeignTarget(...)} + * to not break the method {@link java.nio.file.CopyMoveHelper copyToForeignTarget(...)} * * @param lastModifiedTime * @param lastAccessTime diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystem.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystem.groovy index cf6ffc11..98587d88 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystem.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystem.groovy @@ -58,11 +58,11 @@ final class QuiltFileSystem extends FileSystem implements Closeable { return quiltIDS } - void copy(QuiltPath source, QuiltPath target) { + static void copy(QuiltPath source, QuiltPath target) { throw new UnsupportedOperationException("NOT Implemented 'QuiltFileSystem.copy' `$source` -> `$target`") } - void delete(QuiltPath path) { + static void delete(QuiltPath path) { //log.debug("QuiltFileSystem.delete: $path") path.deinstall() //throw new UnsupportedOperationException("Operation 'delete' is not supported by QuiltFileSystem") @@ -93,7 +93,7 @@ final class QuiltFileSystem extends FileSystem implements Closeable { return QuiltParser.SEP } - QuiltFileAttributesView getFileAttributeView(QuiltPath path) { + static QuiltFileAttributesView getFileAttributeView(QuiltPath path) { //log.debug("QuiltFileAttributesView QuiltFileSystem.getFileAttributeView($path)") String pathString = path.toUriString() try { @@ -105,7 +105,7 @@ final class QuiltFileSystem extends FileSystem implements Closeable { } } - QuiltFileAttributes readAttributes(QuiltPath path) { + static QuiltFileAttributes readAttributes(QuiltPath path) { log.debug("QuiltFileAttributes QuiltFileSystem.readAttributes($path)") Path installedPath = path.localPath() try { @@ -113,12 +113,12 @@ final class QuiltFileSystem extends FileSystem implements Closeable { return new QuiltFileAttributes(path, path.toString(), attrs) } catch (NoSuchFileException e) { - log.debug("No attributes yet for: ${installedPath}") + log.debug("No attributes yet for: ${installedPath}\n$e") } return null } - boolean exists(QuiltPath path) { + static boolean exists(QuiltPath path) { return path.pkg().isInstalled() } @@ -145,15 +145,15 @@ final class QuiltFileSystem extends FileSystem implements Closeable { return new QuiltPath(this, p) } - String toUriString(Path path) { + static String toUriString(Path path) { return path in QuiltPath ? ((QuiltPath)path).toUriString() : null } - String getBashLib(Path path) { + static String getBashLib(Path path) { return path in QuiltPath ? QuiltBashLib.script() : null } - String getUploadCmd(String source, Path target) { + static String getUploadCmd(String source, Path target) { return target in QuiltPath ? QuiltFileCopyStrategy.uploadCmd(source, target) : null } diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystemProvider.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystemProvider.groovy index 5ed5ad7e..8c4d664c 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystemProvider.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltFileSystemProvider.groovy @@ -34,7 +34,6 @@ import java.nio.file.NoSuchFileException import java.nio.file.OpenOption import java.nio.file.Path import java.nio.file.Paths -import java.nio.file.StandardOpenOption import java.nio.file.attribute.BasicFileAttributeView import java.nio.file.attribute.BasicFileAttributes import java.nio.file.attribute.FileAttribute @@ -94,12 +93,12 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr boolean canUpload(Path source, Path target) { log.debug "QuiltFileSystemProvider.canUpload: ${source} -> ${target}" - return FileSystems.getDefault().equals(source.getFileSystem()) && target instanceof QuiltPath + return FileSystems.getDefault() == source.getFileSystem() && target instanceof QuiltPath } boolean canDownload(Path source, Path target) { log.debug "QuiltFileSystemProvider.canDownload: ${source} -> ${target}" - return source instanceof QuiltPath && FileSystems.getDefault().equals(target.getFileSystem()) + return source instanceof QuiltPath && FileSystems.getDefault() == target.getFileSystem() } void download(Path remoteFile, Path localDestination, CopyOption... options) throws IOException { @@ -161,7 +160,7 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr return QuiltParser.SCHEME } - String getQuiltIDS(URI uri) { + static String getQuiltIDS(URI uri) { assert uri QuiltParser parsed = QuiltParser.forURI(uri) return parsed.quiltID().toString() @@ -213,6 +212,7 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr /* groovylint-disable-next-line UnusedMethodParameter */ QuiltFileSystem newFileSystem(String quiltIDS, Map env) throws IOException { + log.debug("newFileSystem $env") final fs = new QuiltFileSystem(quiltIDS, this) fileSystems[quiltIDS] = fs return fs @@ -301,7 +301,7 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr return new QuiltPath(fs, parsed) } - void checkRoot(Path path) { + static void checkRoot(Path path) { if (path == Paths.get('/')) { throw new UnsupportedOperationException("Operation 'checkRoot' not supported on root path") } @@ -316,7 +316,7 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr * @return * @throws IOException */ - void notifyFilePublish(QuiltPath destination) { //, Path source=null) { + static void notifyFilePublish(QuiltPath destination) { //, Path source=null) { final sess = Global.session /* groovylint-disable-next-line Instanceof */ if (sess instanceof Session) { @@ -345,12 +345,13 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr FileChannel channel = FileChannel.open(installedPath, options) return channel } - catch (java.nio.file.NoSuchFileException e) { - log.error("Failed `FileChannel.open`: ${installedPath} <- ${options}") + catch (NoSuchFileException e) { + log.error("Failed `FileChannel.open`: ${installedPath} <- ${options}\n$e") } + return null } - DirectoryStream emptyStream() throws IOException { + static DirectoryStream emptyStream() throws IOException { return new DirectoryStream() { @Override @@ -450,32 +451,32 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr } @Override - def V getFileAttributeView(Path path, Class type, LinkOption... options) { + V getFileAttributeView(Path path, Class type, LinkOption... options) { // log.debug("Calling `getFileAttributeView`: ${path}") checkRoot(path) if (type == BasicFileAttributeView || type == QuiltFileAttributesView) { QuiltPath qPath = asQuiltPath(path) - QuiltFileSystem fs = qPath.filesystem + QuiltFileSystem fs = qPath.getFileSystem() as QuiltFileSystem return (V)fs.getFileAttributeView(qPath) } throw new UnsupportedOperationException("Operation 'getFileAttributeView' is not supported for type $type") } @Override - def A readAttributes(Path path, Class type, LinkOption... options) + A readAttributes(Path path, Class type, LinkOption... options) throws IOException { log.debug 'BasicFileAttributes QuiltFileSystemProvider.readAttributes()' def attr = attributesCache.get(path) if (attr) { - return attr + return attr as A } if (type == BasicFileAttributes || type == QuiltFileAttributes) { QuiltPath qPath = asQuiltPath(path) - QuiltFileSystem fs = qPath.filesystem + QuiltFileSystem fs = qPath.getFileSystem() as QuiltFileSystem def result = (A)fs.readAttributes(qPath) if (result) { attributesCache[path] = result - return result + return result as A } log.debug("readAttributes: File ${qPath.localPath()} not found") if (!qPath.isNull()) { @@ -484,7 +485,7 @@ class QuiltFileSystemProvider extends FileSystemProvider implements FileSystemTr log.warn("readAttributes: Ignore ${qPath} for null bucket") } throw new UnsupportedOperationException("Not a valid Quilt Storage file attribute type: $type") - } + } @Override Map readAttributes(Path path, String attributes, LinkOption... options) throws IOException { diff --git a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltPath.groovy b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltPath.groovy index 256e554b..31b09e56 100644 --- a/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltPath.groovy +++ b/plugins/nf-quilt/src/main/nextflow/quilt/nio/QuiltPath.groovy @@ -21,6 +21,7 @@ import nextflow.quilt.jep.QuiltParser import java.nio.file.Files import java.nio.file.FileSystem import java.nio.file.LinkOption +import java.nio.file.NoSuchFileException import java.nio.file.Path import java.nio.file.Paths import java.nio.file.ProviderMismatchException @@ -112,10 +113,11 @@ final class QuiltPath implements Path, Comparable { log.debug("QuiltPath.deinstall: $path") try { Files.delete(path) - } catch (java.nio.file.NoSuchFileException e) { + } catch (NoSuchFileException e) { // Handle the exception here log.error("Failed to delete path: $path", e) } + return false } @Override diff --git a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltObserverTest.groovy b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltObserverTest.groovy index b7e05243..19caad02 100644 --- a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltObserverTest.groovy +++ b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltObserverTest.groovy @@ -56,6 +56,7 @@ class QuiltObserverTest extends QuiltSpecification { observer.onFlowCreate(mockSession(false)) observer.onFilePublish(badPath) observer.onFlowComplete() + observer.countPublishedPaths() > 0 then: true } @@ -69,8 +70,8 @@ class QuiltObserverTest extends QuiltSpecification { // uninitialized observer.onFilePublish(validPath, validPath.localPath()) then: - validPath.pkg().isBucketAccessible() == true - observer.publishedPaths.size() == 0 + validPath.pkg().isBucketAccessible() + observer.countPublishedPaths() == 0 } void 'should only add publishedPaths if valid path'() { @@ -85,27 +86,27 @@ class QuiltObserverTest extends QuiltSpecification { observer.onFlowCreate(mockSession(false)) observer.onFilePublish(badPath, badPath.localPath()) then: - observer.publishedPaths.size() == 0 + observer.countPublishedPaths() == 0 when: // no source observer.onFilePublish(badPath) then: - observer.publishedPaths.size() == 0 + observer.countPublishedPaths() == 0 when: // local path (treated as overlay) observer.onFilePublish(localPath) then: - observer.publishedPaths.size() == 0 + observer.countPublishedPaths() == 0 when: // valid bucket observer.onFilePublish(validPath, validPath.localPath()) observer.onFlowComplete() then: - validPath.pkg().isBucketAccessible() == true - observer.publishedPaths.size() == 1 + validPath.pkg().isBucketAccessible() + observer.countPublishedPaths() == 1 } } diff --git a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltPkgTest.groovy b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltPkgTest.groovy index 017c4282..778f5415 100644 --- a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltPkgTest.groovy +++ b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltPkgTest.groovy @@ -25,6 +25,8 @@ import java.nio.file.Path import groovy.transform.CompileDynamic +import java.util.jar.Manifest + /** * * @author Ernest Prabhakar @@ -41,8 +43,7 @@ class QuiltPkgTest extends QuiltSpecification { private static QuiltPackage GetPackage(String suffix) { String baseURI = SpecURI().replace('source', suffix) - QuiltPathFactory factory = new QuiltPathFactory() - QuiltPath qpath = factory.parseUri(baseURI) + QuiltPath qpath = QuiltPathFactory.parse(baseURI) QuiltPackage pkg = qpath.pkg() return pkg } @@ -50,7 +51,7 @@ class QuiltPkgTest extends QuiltSpecification { static String manifestVersion() { String subPath = 'src/resources/META-INF/MANIFEST.MF' File manifestFile = new File(subPath) - def manifest = new java.util.jar.Manifest(new FileInputStream(manifestFile)) + def manifest = new Manifest(new FileInputStream(manifestFile)) def attrs = manifest.getMainAttributes() String version = attrs.getValue('Plugin-Version') return version diff --git a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltProductTest.groovy b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltProductTest.groovy index fd83c415..c6c4d86c 100644 --- a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltProductTest.groovy +++ b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltProductTest.groovy @@ -18,6 +18,7 @@ package nextflow.quilt import nextflow.Session import nextflow.script.WorkflowMetadata +import com.quiltdata.quiltcore.workflows.WorkflowException import nextflow.quilt.nio.QuiltPath import nextflow.quilt.nio.QuiltPathFactory @@ -43,13 +44,14 @@ class QuiltProductTest extends QuiltSpecification { WorkflowMetadata wf_meta = GroovyMock(WorkflowMetadata) { toMap() >> [start:'2022-01-01', complete:'2022-01-02'] } + println("makeProductFromUrl: ${url}") QuiltPath path = QuiltPathFactory.parse(url) QuiltPathify pathify = new QuiltPathify(path) Session session = GroovyMock(Session) { getWorkflowMetadata() >> wf_meta getParams() >> [outdir: url] isSuccess() >> success - config >> [quilt: [meta: [cfkey: 'cfval']]] + config >> [quilt: [meta: [cf_key: 'cf_val']]] } return new QuiltProduct(pathify, session) } @@ -62,11 +64,11 @@ class QuiltProductTest extends QuiltSpecification { return makeProductFromUrl(subURL, success) } - QuiltProduct makeConfigProduct(Map config = null) { + QuiltProduct makeConfigProduct(Map qconfig = null) { QuiltPath path = QuiltPathFactory.parse(testURI) QuiltPathify pathify = new QuiltPathify(path) Session session = GroovyMock(Session) - session.config >> config + session.config >> [quilt: qconfig] QuiltProduct product = new QuiltProduct(pathify, session) return product } @@ -85,13 +87,13 @@ class QuiltProductTest extends QuiltSpecification { QuiltProduct product = makeProduct() expect: - product product.pkg product.session != null product.session.getWorkflowMetadata() != null - product.meta != null - product.meta.size() == 4 - product.meta.key == 'val' + product.metadata != null + product.metadata.key == 'val' + product.metadata.key2 == 'val2' + product.displayName().replace('%2f', '/') == testURI } void 'should generate solid string for timestamp from now'() { @@ -103,90 +105,128 @@ class QuiltProductTest extends QuiltSpecification { !now.contains(' ') } - void 'should create from session'() { + void 'should create QuiltProduct from session'() { given: QuiltProduct product = makeProduct() expect: product !product.shouldSkip('key') !product.shouldSkip('missing_key') - !product.shouldSkip(QuiltProduct.KEY_SKIP) + !product.shouldSkip(QuiltProduct.KEY_SUMMARIZE) !product.shouldSkip(QuiltProduct.KEY_README) !product.shouldSkip(QuiltProduct.KEY_META) + product.compileReadme("msg") } - void 'shouldSkip is true if key=SKIP'() { + void 'shouldSkip if key is false'() { given: - QuiltProduct product = makeProduct('readme=SKIP') - expect: - !product.shouldSkip(QuiltProduct.KEY_SKIP) - !product.shouldSkip(QuiltProduct.KEY_META) - product.shouldSkip(QuiltProduct.KEY_README) + Map qconfig = [:] + qconfig[QuiltProduct.KEY_META] = false + qconfig[QuiltProduct.KEY_README] = false + qconfig[QuiltProduct.KEY_SUMMARIZE] = false + println("qconfig: ${qconfig}") + QuiltProduct product = makeConfigProduct(qconfig) - !makeProduct('?readme=now').shouldSkip() + expect: + product.shouldSkip(key) + product.compileReadme("test") == null + + where: + key << [ + QuiltProduct.KEY_META, + QuiltProduct.KEY_README, + QuiltProduct.KEY_SUMMARIZE + ] } - void 'addSessionMeta is false if no config'() { + void 'overrides config meta with query string'() { when: - QuiltProduct no_config = makeConfigProduct() + println('\nOVERRIDE CONFIG META\n') + QuiltProduct product = makeProduct() + then: - no_config.addSessionMeta() == false + product.metadata + product.metadata['cf_key'] == 'cf_val' + product.metadata['key'] == 'val' + product.metadata['key2'] == 'val2' when: - QuiltProduct no_quilt = makeConfigProduct([quilt: null]) + QuiltProduct cf_product = makeProduct('cf_key=override&key3=another') then: - no_quilt.addSessionMeta() == false + cf_product.metadata + cf_product.metadata['cf_key'] == 'override' + cf_product.metadata['key3'] == 'another' + } + + void 'overrides config force with query string'() { + expect: + true + } + + void 'overrides config catalog with query string'() { + expect: + true + } + + void 'skips README if false'() { + expect: + true + } + + void 'overrides default msg with config'() { + expect: + true + } + + void 'match files if present'() { + QuiltProduct product = makeProduct() + String filename = "text.txt" + QuiltProduct.writeString("test", product.pkg, filename) + expect: + product.match("*")[0].toString() == filename + product.match("*.txt")[0].toString() == filename + !product.match('temp.txt') } - @IgnoreIf({ System.getProperty('os.name').toLowerCase().contains('windows') }) - void 'does not create README if readme=SKIP'() { + void 'writeSummarize empty if no files are present'() { given: - QuiltProduct skipREADME = makeProduct('readme=SKIP') - String text = skipREADME.setupReadme() - def files = skipREADME.pkg.folder.list().sort() + QuiltProduct product = makeProduct('readme=SKIP') + product.pkg.reset() expect: - skipREADME.shouldSkip(QuiltProduct.KEY_README) - !text - files.size() == 0 + !product.match('*.md') + product.writeSummarize() == [] } - void 'always creates README if readme!=SKIP'() { + void 'overrides default README with config'() { when: QuiltProduct defaultREADME = makeProduct() - String text = defaultREADME.setupReadme() - def files = defaultREADME.pkg.folder.list().sort() + String text = defaultREADME.writeReadme('msg') + def files = defaultREADME.match('*') then: + text.size() > 0 !defaultREADME.shouldSkip(QuiltProduct.KEY_README) files.size() > 0 when: String readme_text = 'hasREADME' QuiltProduct hasREADME = makeProduct("readme=${readme_text}") - text = hasREADME.setupReadme() - files = hasREADME.pkg.folder.list().sort() + text = hasREADME.writeReadme('msg') + files = hasREADME.match('*') then: text == readme_text !hasREADME.shouldSkip(QuiltProduct.KEY_README) files.size() > 0 } - void 'setupSummarize empty if no files are present'() { - given: - QuiltProduct product = makeProduct('readme=SKIP') - product.pkg.reset() - expect: - !product.match('*.md') - product.setupSummarize() == [] - } void 'should create summarize if files are present'() { String readme_text = 'hasREADME' QuiltProduct product = makeProduct("readme=${readme_text}") - product.setupReadme() + product.writeReadme('msg') product.match('*.md') - List quilt_summarize = product.setupSummarize() + List quilt_summarize = product.writeSummarize() expect: quilt_summarize quilt_summarize.size() == 1 @@ -198,8 +238,8 @@ class QuiltProductTest extends QuiltSpecification { QuiltProduct product = makeWriteProduct() String filename = 'test.md' String text = 'test' - Path src = Paths.get(product.pkg.folder.toString(), filename) - Path dest = Paths.get(product.pkg.folder.toString(), 'copy', filename) + Path src = Paths.get(product.pkg.packageDest().toString(), filename) + Path dest = Paths.get(product.pkg.packageDest().toString(), 'copy', filename) Files.writeString(src, text) when: @@ -224,7 +264,7 @@ class QuiltProductTest extends QuiltSpecification { when: makeWriteProduct() // no metadata then: - thrown(com.quiltdata.quiltcore.workflows.WorkflowException) + thrown(WorkflowException) expect: makeWriteProduct(meta) // valid metadata @@ -232,17 +272,17 @@ class QuiltProductTest extends QuiltSpecification { when: makeWriteProduct() // invalid default metadata then: - thrown(com.quiltdata.quiltcore.workflows.WorkflowException) + thrown(WorkflowException) when: makeWriteProduct(bad_meta) // invalid explicit metadata then: - thrown(com.quiltdata.quiltcore.workflows.WorkflowException) + thrown(WorkflowException) when: makeWriteProduct(skip_meta) // no default metadata then: - thrown(com.quiltdata.quiltcore.workflows.WorkflowException) + thrown(WorkflowException) // NOTE: push does NOT update local registry expect: @@ -251,10 +291,10 @@ class QuiltProductTest extends QuiltSpecification { when: makeWriteProduct(skip_meta) // still fails on implicit metadata then: - thrown(com.quiltdata.quiltcore.workflows.WorkflowException) + thrown(WorkflowException) } - void writeFile(root, filename) { + void writeFile(String root, String filename) { Path outPath = Paths.get(root, filename) outPath.getParent().toFile().mkdirs() Files.writeString(outPath, "#Time, Filename\n${timestamp},${filename}") @@ -288,7 +328,8 @@ class QuiltProductTest extends QuiltSpecification { and: Session session = Mock(Session) QuiltPath path = QuiltPathFactory.parse(sumURL) - QuiltProduct product = new QuiltProduct(path, session) + QuiltPathify pathify = new QuiltPathify(path) + QuiltProduct product = new QuiltProduct(pathify, session) expect: product.publish() @@ -296,35 +337,17 @@ class QuiltProductTest extends QuiltSpecification { Files.exists(Paths.get(sumPkg.packageDest().toString(), QuiltProduct.SUMMARY_FILE)) } - void 'should getMetadata from Map'() { - given: - Map meta = [ - 'Name': 'QuiltPackageTest', - 'Owner': 'Ernest', - 'Date': '1967-10-08', - 'Type': 'NGS' - ] - QuiltProduct product = makeProduct() - Map quilt_meta = product.getMetadata(meta) - - expect: - quilt_meta != null - quilt_meta.config == meta - } - - void 'should addSessionMeta from session'() { + void 'should concatMetadata from session'() { when: QuiltProduct product = makeProduct('a=b&c=d') - Map start_meta = product.meta + Map start_meta = product.metadata then: start_meta != null - start_meta.size() == 4 start_meta.a == 'b' - product.addSessionMeta() when: - Map end_meta = product.meta + Map end_meta = product.collectMetadata() then: end_meta != null @@ -332,7 +355,7 @@ class QuiltProductTest extends QuiltSpecification { end_meta.a == 'b' } - void 'should throw error on publish'() { + void 'should not throw error on publish'() { given: QuiltProduct product = makeProduct() @@ -340,15 +363,15 @@ class QuiltProductTest extends QuiltSpecification { product.publish() then: - thrown(RuntimeException) + true } - void 'should throw error if session.isSuccess'() { + void 'should not throw error if session.isSuccess'() { when: - makeProduct(query: null, success: true) + makeProduct('', true) then: - thrown(RuntimeException) + true } } diff --git a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltSpecification.groovy b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltSpecification.groovy index 0f8f3aea..6d531d49 100644 --- a/plugins/nf-quilt/src/test/nextflow/quilt/QuiltSpecification.groovy +++ b/plugins/nf-quilt/src/test/nextflow/quilt/QuiltSpecification.groovy @@ -65,7 +65,7 @@ class QuiltSpecification extends Specification { // this need to be set *before* the plugin manager class is created testURI = 'quilt+s3://bkt?key=val&key2=val2' + '#package=pre/suf@abcdef314159265'+ - '&path=p/t&property=prop&workflow=wf&catalog=quiltdata.com' + '&path=p/t&workflow=wf&catalog=quiltdata.com' pluginsMode = System.getProperty('pf4j.mode') timestamp = System.currentTimeMillis() writeBucket = System.getenv('WRITE_BUCKET') diff --git a/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltPackageTest.groovy b/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltPackageTest.groovy index e3199ec9..cf2d5f95 100644 --- a/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltPackageTest.groovy +++ b/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltPackageTest.groovy @@ -40,7 +40,8 @@ class QuiltPackageTest extends QuiltSpecification { private final static String PACKAGE_URL = 'quilt+s3://quilt-example#package=examples%2fsmart-report@d68a7e9' private final static String TEST_URL = PACKAGE_URL + '&path=README.md' - private final static String READONLY_URL = 'quilt+s3://allencell#package=test%2ftmp&path=foo%2fbar.txt' + private final static PKG = 'aics_mnist' + private final static String READONLY_URL = "quilt+s3://allencell#package=aics%2f${PKG}&path=${PKG}.ipynb" private QuiltPathFactory factory private QuiltPath qpath @@ -48,7 +49,7 @@ class QuiltPackageTest extends QuiltSpecification { void setup() { factory = new QuiltPathFactory() - qpath = factory.parseUri(TEST_URL) + qpath = QuiltPathFactory.parse(TEST_URL) pkg = qpath.pkg() } @@ -67,7 +68,7 @@ class QuiltPackageTest extends QuiltSpecification { void 'should distinguish Packages with same name in different Buckets '() { given: def url2 = TEST_URL.replace('quilt-', 'quilted-') - def qpath2 = factory.parseUri(url2) + def qpath2 = QuiltPathFactory.parse(url2) def pkg2 = qpath2.pkg() expect: @@ -90,7 +91,7 @@ class QuiltPackageTest extends QuiltSpecification { void 'should get attributes for package folder'() { given: def root = qpath.getRoot() - def qroot = factory.parseUri(PACKAGE_URL) + def qroot = QuiltPathFactory.parse(PACKAGE_URL) expect: root == qroot qroot.isJustPackage() @@ -101,7 +102,7 @@ class QuiltPackageTest extends QuiltSpecification { void 'should not raise error on null bucket'() { given: QuiltPackage.resetPackageCache() - def qpath = factory.parseUri('quilt+s3://./') + def qpath = QuiltPathFactory.parse('quilt+s3://./') def pkg = qpath.pkg() expect: pkg.isNull() @@ -121,7 +122,7 @@ class QuiltPackageTest extends QuiltSpecification { void 'should download the specified path'() { given: - def qpath = factory.parseUri(TEST_URL) + def qpath = QuiltPathFactory.parse(TEST_URL) def qpkg = qpath.pkg() Path outputFolder = pkg.packageDest() Path readmeFile = outputFolder.resolve('README.md') @@ -144,7 +145,7 @@ class QuiltPackageTest extends QuiltSpecification { void 'should return null on failed implicit install'() { given: def url2 = TEST_URL.replace('quilt-', 'quilted-') - def qpath2 = factory.parseUri(url2) + def qpath2 = QuiltPathFactory.parse(url2) def pkg2 = qpath2.pkg() expect: @@ -154,7 +155,7 @@ class QuiltPackageTest extends QuiltSpecification { void 'should throw error on explict install'() { given: def url2 = TEST_URL.replace('quilt-', 'quilted-') - def qpath2 = factory.parseUri(url2) + def qpath2 = QuiltPathFactory.parse(url2) def pkg2 = qpath2.pkg() when: @@ -180,7 +181,7 @@ class QuiltPackageTest extends QuiltSpecification { void 'should iterate over installed files '() { given: def root = qpath.getRoot() - def qroot = factory.parseUri(PACKAGE_URL) + def qroot = QuiltPathFactory.parse(PACKAGE_URL) expect: root @@ -192,10 +193,10 @@ class QuiltPackageTest extends QuiltSpecification { void 'should fail pushing new files to read-only bucket '() { given: - def qout = factory.parseUri(READONLY_URL) + def qout = QuiltPathFactory.parse(READONLY_URL) def opkg = qout.pkg() opkg.install() - def outPath = Paths.get(opkg.packageDest().toString(), 'foo/bar.txt') + def outPath = Paths.get(opkg.packageDest().toString(), "${PKG}.ipynb") Files.writeString(outPath, "Time: ${timestamp}") expect: Files.exists(outPath) @@ -212,7 +213,7 @@ class QuiltPackageTest extends QuiltSpecification { expect: opkg opkg.is_force() - opkg.bucket == writeBucket + opkg.bucketAccessible opkg.packageName.contains('test/observer') } diff --git a/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltParserTest.groovy b/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltParserTest.groovy index 2b97167b..95eb5361 100644 --- a/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltParserTest.groovy +++ b/plugins/nf-quilt/src/test/nextflow/quilt/jep/QuiltParserTest.groovy @@ -30,15 +30,6 @@ class QuiltParserTest extends QuiltSpecification { thrown(IllegalArgumentException) } - void 'should parse over-long packages into path'() { - when: - QuiltParser parser = QuiltParser.forUriString(TEST_URL) - then: - parser.getBucket() == 'quilt-ernest-staging' - parser.getPackageName() == 'nf-quilt/sarek' - parser.getPath() == 'pipeline_info/execution_trace_2022-10-13_01-01-31.txt' - } - void 'should modify path segments appropriately'() { when: QuiltParser parser = QuiltParser.forUriString(REL_URL) @@ -106,7 +97,6 @@ class QuiltParserTest extends QuiltSpecification { parser.getBucket() == 'bkt' parser.getPackageName() == 'pre/suf' parser.getPath() == 'p/t' - parser.getPropertyName() == 'prop' parser.getWorkflowName() == 'wf' parser.getCatalogName() == 'quiltdata.com' meta @@ -117,10 +107,12 @@ class QuiltParserTest extends QuiltSpecification { void 'should unparse other parameters back to URI'() { when: QuiltParser parser = QuiltParser.forUriString(testURI) - String unparsed = parser.toUriString() + String unparsed = parser.toUriString().replace('%2f', '/') + String no_scheme = testURI.replace('quilt+s3://','') then: - unparsed.replace('%2f', '/') == testURI + unparsed == testURI + parser.toString() == no_scheme.replace('/','%2f') } void 'should collect array parameters from query string'() {