Skip to content

Replace shell-based context with profile-based configuration - #300

Open
alexiswl wants to merge 21 commits into
devfrom
feature/migrate-to-profile-based-contexts
Open

Replace shell-based context with profile-based configuration#300
alexiswl wants to merge 21 commits into
devfrom
feature/migrate-to-profile-based-contexts

Conversation

@alexiswl

Copy link
Copy Markdown
Member

Summary

Replaces the shell-function-based tenant/project context management system with an AWS CLI-style profile configuration approach. The CLI becomes a standalone executable that reads configuration from disk and manages token lifecycle internally — no shell function sourcing required.

Key Changes

New: Profile-based configuration system

  • INI-style config file at ~/.icav2-cli-plugins/config with [default] and [profile <name>] sections
  • Precedence chain: env vars → --profile flag → ICAV2_PROFILEICAV2_TENANT_NAME[default]
  • Per-profile token caching in ~/.icav2-cli-plugins/cache/<profile>/session.yaml
  • JWT freshness checks (3600s threshold) with automatic regeneration from API key

New: configure command group

  • icav2 configure set [<profile_name>] — interactive profile setup with API key validation
  • icav2 configure list — display configured profiles

Refactored: CLI entry point (utils/cli.py)

  • Standalone operation with --profile global flag
  • Lazy import dispatcher for fast startup (<500ms for help/version)
  • Delegation to bundled _icav2 binary via os.execve
  • Deferred heavy imports in subcommands/__init__.py

Updated: Installer (install.sh)

  • Downloads and bundles platform-specific icav2 binary to ~/.icav2-cli-plugins/bin/_icav2
  • Creates cache directory structure
  • Removes command icav2 prerequisite (now self-contained)
  • Migration prompt for existing tenants/ configurations
  • New --skip-binary flag for pre-installed environments

Testing

  • Property-based tests (hypothesis) validating 11 correctness properties from design doc
  • Unit tests for CLI, configure commands, token manager, delegation, deferred imports, installer

Backward Compatibility

  • ICAV2_ACCESS_TOKEN, ICAV2_PROJECT_ID, ICAV2_BASE_URL env vars continue to work (highest precedence)
  • ICAV2_TENANT_NAME treated as profile name for backward compat
  • Existing tenants/ directory preserved; migration is opt-in

Commits

  1. Add spec documents for profile-based configuration feature
  2. Add config parser and profile constants
  3. Implement profile resolver with precedence chain
  4. Implement token manager with caching and JWT validation
  5. Refactor CLI entry point with lazy imports and profile integration
  6. Add configure set and configure list subcommands
  7. Update installer for bundled binary and profile-based architecture
  8. Add property-based and unit tests for profile-based configuration

alexiswl and others added 21 commits August 5, 2024 19:11
Create hotfix for projectdata path error
Update import paths across config_helpers, test_config_helpers,
bundle_helpers, bundles_deprecate, and bundles_release to use
libica.openapi.v3 Configuration and ApiException instead of v2.

Also replace wrapica.libica_exceptions.ApiException with the
direct libica.openapi.v3.ApiException import.
- Replace ProjectPipeline with ProjectPipelineType in subcommands/__init__.py
- Add separate handling for ProjectPipelineType vs PipelineType coercion
- Update analysis storage coercion to use keyword arguments
- Add 'return value' after analysis coercion to fix early-return bug
- Cast pipeline status to PipelineStatusType in bundles_add_pipeline
- Use str() for UUID comparisons across bundle, pipeline, and project
  operations to handle model objects correctly
The clean_multi_args workaround for docopt issue #134 is no longer
used after the commented-out call was removed from Command.__init__.
- libica >= 3, < 4 (was >= 2.4.0, < 3)
- wrapica >= 2.40.1, < 2.41.0 (was >= 2.27.1)
- cwl_utils >= 0.38 (was >= 0.32)
- Add pip, pandas-stubs, boto3-stubs, mypy-boto3-s3 dependencies
- Add --include-linked flag to show linked data from other projects
- Add --case-insensitive flag for case-insensitive path filtering
- Display '@' suffix for linked files in short listing mode
- Use parent_folder_id when available for more precise listing
- Update sort parameter to use ProjectDataSortParameterType literals
- Fix projectdata_helpers to use typed AwsTempCredentials attributes
  instead of dict.get() calls
- Fix s3 sync script trailing slash and arg indentation
- s3-sync-download/upload: use AwsTempCredentials typed attributes,
  rename CLI args to local_download_path/local_upload_path,
  initialize s3_sync_args to empty list when not provided
- view: fix BROWSER env var check logic (was inverted)
- find: fix creator_id comparison to use str() for UUID objects
- cp: copy files/directories within a project using the ICAv2 API,
  with job completion waiting and optional JSON output
- mkdir: create directories in ICAv2 project data
projectpipelines:
- launch_wes: support both CWL and Nextflow analysis types,
  add --cache option, comment out deprecated samplesheet_input logic,
  use keyword args for analysis_storage coercion
- pipelines_update: add icav2.config auto-injection for Nextflow,
  filter hidden/test files from new file detection,
  use ProjectPipelineV4 model, fix owner_id comparison
- create_cwl_workflow_from_zip: sanitize dots in pipeline code
- create_nextflow_pipeline_from_nf_core: fix analysis_storage naming
- create_nextflow_pipeline_from_zip: make --workflow-description required
- create_wes_input_template: rename samplesheet_input to samplesheet,
  stringify pipeline id in engine parameters
- download_pipeline: make output_dir optional with cwd default

projectanalyses:
- get_step_logs: update step name default, use typed AnalysisStep,
  fix log_obj filtering to use .name attribute, use string log names
- list_v2: keep time_modified column in non-long listing
- Rename subcommand from 'start-cwl-wes' to 'start-wes' in
  autocompletion specs (bash, zsh, yaml) and README
- Add ICAV2_SERVER_URL export to tenants enter shell function
- Update _icav2 shell function command list: add download,
  rename nfcore variant, add nextflow-pipeline-from-zip
- Remove stale FIXME comments in tenants enter
- Add jq version check (>= 1.6)
- Add icav2 CLI version check (>= 2.30.0)
- Fix shell detection in source.sh to use /proc/65994/exe readlink
  instead of $SHELL env var (handles cases where login shell
  differs from running shell)
- Use prefixed variable names to avoid polluting user's namespace
- Unset temporary variables after use in generated source.sh
- Add readlink/greadlink availability check in source.sh
Add product overview, project structure, and tech stack documentation
as steering files to guide AI-assisted development.
Add requirements, design, and implementation task documents for the
profile-based configuration system that replaces shell-function-based
tenant/project context management with an AWS CLI-style profile approach.

Key design decisions:
- INI-style config file at ~/.icav2-cli-plugins/config
- Named profiles with precedence chain (env vars > --profile > ICAV2_PROFILE > default)
- Token caching per profile with JWT freshness checks
- Lazy imports for fast CLI startup (<500ms for help/version)
- Bundled _icav2 binary for standalone operation
- Add hypothesis and pytest to [project.optional-dependencies] test group
- Add profile-based configuration constants to globals.py:
  CONFIG_FILE_PATH, CACHE_DIR, BIN_DIR, LOCAL_BINARY_PATH,
  PROFILE_NAME_REGEX, VALID_OUTPUT_FORMATS, TOKEN_REFRESH_THRESHOLD
- Defer wrapica.enums import in globals.py via get_default_analysis_storage_size()
- Implement ConfigParser with AWS CLI-style INI format support:
  - [default] and [profile <name>] section headers
  - Comment handling (# and ;), duplicate key resolution (last wins)
  - ConfigParseError with line numbers for malformed content
  - Round-trip serialize/parse with file I/O (mode 0600)
Add ProfileResolver that determines active configuration by applying:
1. Explicit env vars (ICAV2_ACCESS_TOKEN, ICAV2_PROJECT_ID, ICAV2_BASE_URL)
2. --profile CLI flag
3. ICAV2_PROFILE env var
4. ICAV2_TENANT_NAME env var (backward compat, treated as profile name)
5. [default] profile in config file

Includes ResolvedConfig dataclass with server_url, base_url, access_token,
project_id, api_key, and output_format. Provides actionable error messages
listing available profiles when a requested profile is not found.
Add TokenManager that handles access token lifecycle per profile:
- Cache tokens in ~/.icav2-cli-plugins/cache/<profile>/session.yaml
- Check JWT exp claim with 3600s freshness threshold
- Generate new tokens from API key via ICAv2 API when stale
- Validate ICAV2_ACCESS_TOKEN env var (reject expired/malformed, no fallback)
- Graceful handling of malformed cache files (treat as absent)
- File permissions set to 0600 for security
- Rewrite cli.py main() as standalone entry point:
  - Parse --profile and --debug global args
  - Handle help/version early without heavy imports
  - Integrate ProfileResolver and TokenManager
  - Dispatch to plugin subcommands or delegate to _icav2 binary
  - Use os.execve for binary delegation (replaces subprocess.run)
- Add COMMAND_MODULE_MAP with lazy_import_command() dispatcher
- Add 'configure' command group to CLI docstring
- Defer wrapica/libica/pandas imports in subcommands/__init__.py:
  - Move heavy imports to point of first use in coerce_magical_value()
  - Command and DocOptArg classes importable without triggering library loads
- Fix type cast in projectanalyses/list_steps.py for project_id
- configure set: Interactive profile configuration with prompts for
  server_url, x_api_key, and project_name. Validates API key before
  saving. Creates config file with mode 0600 if absent.
- configure list: Displays table of all configured profiles showing
  name, server_url, and project info. Shows guidance message when
  no profiles are configured.
- Command registration in configure/__init__.py with docopt dispatch
- Add --skip-binary flag for environments with pre-installed icav2
- Add platform/architecture detection (linux/darwin, amd64/arm64)
- Download icav2 binary to ~/.icav2-cli-plugins/bin/_icav2
- Create cache/ directory for per-profile token storage
- Create empty config file with mode 0600 if not present
- Remove 'command icav2' prerequisite check (now self-contained)
- Add migration prompt for existing tenants/ directory
- Simplify source.sh: set ICAV2_CLI_PLUGINS_HOME and prepend bin/ to PATH
- Abort setup if binary download fails
Property-based tests (hypothesis):
- Config parser: round-trip, comment invariance, duplicate key resolution,
  malformed line detection, output format validation
- Profile resolver: default values, profile selection, precedence resolution,
  invalid profile error
- Token manager: freshness decision, expired JWT rejection

Unit tests:
- CLI: lazy imports, startup performance, --profile parsing, help/version
- Configure commands: set (with mocked stdin/API), list (format/empty state)
- Token manager: caching, refresh, generation, env var validation
- Delegation: _icav2 binary execution with env vars
- Deferred imports: heavy libraries not loaded at import time
- Installer: platform detection, directory structure, migration prompt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant