├── LICENSE.txt ├── package.json ├── design_docs.md ├── README.md └── apex-source-control /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 http://ntree.com/ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apex-source-control", 3 | "version": "1.4.2", 4 | "description": "Scripts for bringing Oracle Application Express apps into source control", 5 | "bin": { 6 | "apex-source-control": "apex-source-control" 7 | }, 8 | "scripts": { 9 | "apex-to-file": "apex-source-control apex-to-file", 10 | "file-to-apex": "apex-source-control file-to-apex", 11 | "new-conf-file": "apex-source-control new-conf-file", 12 | "switch-conf-file": "apex-source-control switch-conf-file", 13 | "read-conf-file": "apex-source-control read-conf-file", 14 | "generate-app-id": "apex-source-control generate-app-id", 15 | "uninstall-apex": "apex-source-control uninstall-apex" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/ntreeinc/apex-source-control.git" 20 | }, 21 | "keywords": [ 22 | "oracle", 23 | "apex", 24 | "application", 25 | "express", 26 | "version", 27 | "source", 28 | "control" 29 | ], 30 | "author": "Ntree Inc. (http://ntree.com/)", 31 | "contributors": [ 32 | "Nathan Farrant-Diaz (https://github.com/nfarrantdiaz)" 33 | ], 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/ntreeinc/apex-source-control#known-issues" 37 | }, 38 | "homepage": "https://github.com/ntreeinc/apex-source-control" 39 | } 40 | -------------------------------------------------------------------------------- /design_docs.md: -------------------------------------------------------------------------------- 1 | **The problem**: 2 | 3 | There is a common practice of not placing databases or back-end applications under source-control management (SCM). APEX is web application development tool designed for back-end developers with limited front-end experience so the culture of ignoring SCM permeates into the development process. This tool aims to make integrating APEX apps with SCM a painless process. 4 | 5 | 6 | **System requirements for use**: 7 | 8 | - A Linux or Mac OSX environment 9 | - Downloaded APEX 5.0 files or above 10 | - An `export APEX_HOME` with `$APEX_HOME/utilities/oracle/apex/*.class` being the location of the above mentioned files 11 | - An installation of Oracle Instant Client 12 | - An `export ORACLE_HOME` with an existing `$ORACLE_HOME/jdbc/lib/jdbc6.jar` (likely included in installation of Oracle Instant Client) 13 | - `sqlplus` on path 14 | 15 | 16 | **What users should already know**: 17 | 18 | - How to use any SCM tool such as git or subversion 19 | - How to use APEX (obviously) 20 | - Very basic understanding of the command line 21 | 22 | 23 | **Tools/APIs we use**: 24 | 25 | Here are the pre-existing tools we used to help make these scripts 26 | 27 | *APEXExport & APEXExportSplitter* 28 | 29 | No docs for these classes but here is a quick overview discovered through trial & error and research: Export allows you to export an app from APEX into a large SQL file using the command line, and Splitter splits that large SQL file into multiple files which fit into a nice directory structure. 30 | 31 | *apex_application_install* 32 | 33 | Again there are no docs but read this blog post by Joel Kallman from Oracle on the tool http://joelkallman.blogspot.ca/2010/07/apexapplicationinstall.html. A quick overview is that essentially this comes with a bunch of pre-packaged functionality useful for installing from a file(s) generated by APEXExport & APEXExportSplitter into APEX. 34 | 35 | *Npm Scripts* 36 | 37 | Used as a front end runner for our scripts read: https://docs.npmjs.com/cli/run-script. 38 | 39 | 40 | **Scripts we create…** 41 | 42 | For each script we talk about 1. its functionality and 2. How it works. And occasionally why it’s needed if not immediately obvious. 43 | 44 | (Note: there are more scripts than this but those are mostly for style and calling from other scripts and not listed here) 45 | 46 | *...for dealing with APEX:* 47 | - file-to-apex 48 | - Converts a file directory generated by APEXExport(Splitter) into an APEX app 49 | - Uses apex_application_install to set import config data (specified in the user’s config file) such as app_alias, workspace_id, and application_id 50 | - apex-to-file 51 | - Converts an APEX app into files 52 | - Checks for proper config and then calls APEXExport & APEXExportSplitter 53 | - uninstall-apex 54 | - Uninstalls your app from APEX 55 | - Calls the delete_application and end_environment scripts generated by Splitting the file generated by APEXExport 56 | 57 | *...for user convenience:* 58 | - new-conf-file 59 | - Creates a new config file based on user input 60 | - Reduces chances of typos in config file creation and was all around convenient during the development process 61 | - Gathers user input for each config value and writes it to a file 62 | - switch-conf-file 63 | - Switches the config file being used 64 | - Was convenient and time-saving during development so I figured it would be nice to expose this functionality to the user as well 65 | - A config file is referenced by an asc.conf symlink, this script lists the available configs and changes the symlink to point to the new file of the user’s choice 66 | - generate-app-id 67 | - Generates an application ID that is unused in your copy of APEX 68 | - Solves the problem of accidentally overwriting another developers app since an unused app id can always be generated 69 | - Exposes the apex_application_install.generate_application_id functionality to the user 70 | - read-conf-file 71 | - States which config file is being used and lists the contents 72 | - Again it was nice to type a single short command rather than 2 longer ones. 73 | - readlink asc.conf && cat asc.conf 74 | 75 | 76 | **Problems faced during creation and our solutions**: 77 | 78 | *1) Application Object ids change on every export causing large conflicts in source control* 79 | 80 | We use APEX's -expOrginalIds option, and since APEX 5.0 seems to have a mechanism for dealing with conflicting Ids (tested by importing several applications using the same Object Ids) we can import into Oracle using the original Ids (is this problem even one that the user needs to know about? Since the problem basically just comes from the paper but turns out to not be an issue it seems as if we just created an issue out of nothing) 81 | 82 | *2) Can’t import into APEX without workspace_id* 83 | 84 | (Believe me, just figuring out that this was the problem without any proper docs was an entire problem of its own) 85 | We use PL/SQL to find the workspace_id (using the user input for workspace) and set the app’s workspace_id as such. Solved in file-to-apex script. 86 | 87 | *3) Possibility of overwriting another developer’s app by picking the same app_id* 88 | 89 | We create a script which automatically generates an unused app_id by exposing the functionality of apex_application_install.generate_application_id. Solved in generate-app-id. 90 | 91 | *4) App-aliases must be unique across workspace* 92 | 93 | We auto generate the app_alias using the app_id. Since the app_id must be unique this ensures that the app_alias will also be unique. We use the string ‘F’ + app_id as the alias. Solved in file-to-apex script. 94 | 95 | *5) When importing to certain servers (like prod) we want to have control over the app_alias* 96 | 97 | We add an optional app_alias value to the config file. If filled in we set the alias to the given value, otherwise we use the auto generated one described previously. Solved in file-to-apex script. 98 | 99 | *6) How to generalize (parameterize) scripts without causing source-control noise* 100 | 101 | We create config files and introduce a symlink which points to the config file which needs to be used. We then warn the user to ignore the symlink (and probably the other config files) in order to remove all possible source-control noise. Now the only noise should come the APEX app itself, that is, we are not adding any extra noise ourselves with these scripts. 102 | 103 | *7) There exists the possibility of overwriting work* 104 | 105 | Further explanation: If I were to make changes in APEX and then, without running apex-to-file, pull from the main SCM repo and run file-to-apex I would lose all my changes without any warning. 106 | Solution: The current solution is to encourage proper workflow practices with users, especially frequent committing. This falls under the “user ought to know how to use SCM” responsibility. 107 | Further Improvement: It would be possible to, after pulling from the main SCM repo run a apex-to-file, check the two versions against each other, and force the user to merge any conflicts properly. 108 | 109 | *8) Code is not easily sharable and could result in many different versions of the same code causing confusion and conflicting usage* 110 | 111 | We use npm to package the scripts as a node module. 112 | 113 | 114 | 115 | 116 | **Unsolved issues/future improvement**: 117 | *1) Random noise in app meta-data* 118 | 119 | To reproduce: 120 | 121 | - Have two developers working on their own version of an app 122 | - Developer 1 makes no change, runs apex-to-file, commits, and pushes to SCM 123 | - Developer 2 makes no changes and pulls from SCM to incorporate Developer 1’s changes 124 | - There are now conflicts shown in the merge attempt, most likely in ./apex/application/create_application.sql and some randomly generated empty sql files, despite none actually existing. 125 | 126 | Why it occurs: 127 | 128 | Every APEX app comes with meta-data; such as who last edited the app and when, what workspace ID was this last edited in, etc.; this obviously changes with every edit so SCM will always detect conflicts on every single attempt to merge. 129 | 130 | How to avoid/fix: 131 | 132 | There is no easy avoidal mechanism or fix for this issue. One possible solution is to write scripts which detect garbage conflicts and automatically deal with them; however, while some conflicts are consistent and predictable, others occur seemingly at random. 133 | 134 | *2) Deleting a page will always win merges* 135 | 136 | To reproduce: 137 | 138 | - Have two developers working on their own versions of an app (i.e. working on different features to be added). 139 | - Developer 1 deletes a page in the app and pushes to the project's repo. 140 | - Developer 2 merges developer 1's changes and decides to keep the deleted page in the merge. 141 | - When developer 2 reinstalls the app into apex the page will still be deleted. 142 | 143 | Why it occurs: 144 | 145 | Since a page was deleted, it's removed from the install.sql script generated by apex. When git sees the merge of the two developers, only one has changed the install.sql script so it automatically applies those changes to the merge meaning the page is no longer called during install so it's never created in your apex application. 146 | 147 | How to avoid/fix: 148 | 149 | Deleting a page from an app is an important decision in any development process thus you should consult with your team before doing so. If you do end up deleting a page you will have to manually re-add the install lines the install lines to the install.sql script (found in /your/project/dir/apex/install.sql). 150 | 151 | *3) Config files on top project level are kind of gross* 152 | 153 | It might be a good idea to keep the config files in a place where the user cannot easily see them. Before this can be implemented however we would need to create an `edit-conf-file` function so the user can interact with config files without ever actually needing to touch one of them (all “touching” done through our provided scripts). 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # apex-source-control 2 | Introducing file-based version-control for Oracle Application Express apps! 3 | 4 | * Designed to be used by teams who want to bring their experience in version-control and APEX development together 5 | * Any version-control tool can be used, not just git 6 | * Tested on Linux and OSX (Windows not supported) 7 | 8 | # Obsolete 9 | This is obsolete now that SQLcl includes the application export functionality. 10 | Running `apex export -skipExportDate -expOriginalIds -split -splitNoCheckSum -applicationid $apexappid` 11 | will give the same result except for replacing absolute paths with relative paths in the scripts for git. 12 | 13 | ## Getting Started 14 | Prerequisites: 15 | * Oracle SQLcl on the path (https://www.oracle.com/database/technologies/appdev/sqlcl.html) 16 | 17 | If you don't/can't use SQLcl, this also still supports the legacy tools from APEX 5. To use them instead of sqlcl, you require: 18 | * APEXExport.class and APEXExportSplitter.class which come with [APEX 5.0] (http://www.oracle.com/technetwork/developer-tools/apex/downloads/index.html) and above 19 | * An `export APEX_HOME` with `$APEX_HOME/utilities/oracle/apex/*.class` being the location of the above mentioned files 20 | * An installation of [Oracle Instant Client](http://www.oracle.com/technetwork/database/features/instant-client/index-100365.html) 21 | * An `export ORACLE_HOME` with an existing `$ORACLE_HOME/jdbc/lib/jdbc6.jar` (likely included in installation of Oracle Instant Client) 22 | * `sqlplus` on path 23 | 24 | ## Usage 25 | #### Bring an existing APEX app into source control 26 | 27 | 1) Make a local directory for your app and in the new directory run `npm init` and follow the given prompts 28 | 29 | 2) Add the following lines to your devDependencies in package.json (Note: delete or repurpose the pre-existing scripts value) 30 | ``` 31 | "scripts": { 32 | "apex-to-file" : "apex-source-control apex-to-file", 33 | "file-to-apex" : "apex-source-control file-to-apex", 34 | "new-conf-file" : "apex-source-control new-conf-file", 35 | "switch-conf-file" : "apex-source-control switch-conf-file", 36 | "read-conf-file" : "apex-source-control read-conf-file", 37 | "generate-app-id" : "apex-source-control generate-app-id", 38 | "uninstall-apex" : "apex-source-control uninstall-apex" 39 | }, 40 | ``` 41 | If you want you can change the npm run commands (under scripts) to anything you'd like. 42 | 43 | 3) Run the following commands and follow the prompts. The app id, parsing_schema, workspace_name and database connection info should all correspond to the app you want to download (see Configuration) 44 | ``` 45 | npm install --save-dev apex-source-control 46 | npm run new-conf-file #create a config file with the info of the app you want to download 47 | npm run switch-conf-file #unnecessary if you choose to switch to your new config file in npm run in previous command 48 | npm run apex-to-file #download the app locally 49 | ``` 50 | You can now set up the application as a git or other version-control repository. Be warned that if you downloaded the app from another developer's copy of the app, or some other version of the app you don't want to overwrite, you should create a new config file and set up a new version of the application. 51 | 52 | #### Setting up from an APEX export file 53 | 54 | 1) Make a local directory for your app and in the new directory run `npm init` and follow the given prompts 55 | 56 | 2) Add the following lines to your devDependencies in package.json (Note: delete or repurpose the pre-existing scripts value) 57 | ``` 58 | "scripts": { 59 | "apex-to-file" : "apex-source-control apex-to-file", 60 | "file-to-apex" : "apex-source-control file-to-apex", 61 | "new-conf-file" : "apex-source-control new-conf-file", 62 | "switch-conf-file" : "apex-source-control switch-conf-file", 63 | "read-conf-file" : "apex-source-control read-conf-file", 64 | "generate-app-id" : "apex-source-control generate-app-id", 65 | "uninstall-apex" : "apex-source-control uninstall-apex" 66 | }, 67 | ``` 68 | If you want you can change the npm run commands (under scripts) to anything you'd like (see npm scripts Commands). 69 | 70 | 3) Run `npm install --save-dev apex-source-control` 71 | 72 | 4) Copy the export file into your project directory 73 | 74 | 5) Set up your classpath and run APEXExportSplitter on the export file 75 | ``` 76 | export CLASSPATH=$APEX_HOME/utilities:$ORACLE_HOME/jdbc/lib/ojdbc6.jar 77 | java oracle.apex.APEXExportSplitter $export_file 78 | ``` 79 | The CLASSPATH variable does not need to be added to your bash profile 80 | 81 | 6) Rename the generated directory to apex/ 82 | 83 | 7) cd into apex/ and run `sed -i 's^@application^@apex/application^g' install.sql` 84 | 85 | We do this because we need to set the relative path to the install components from the top level directory 86 | 87 | 8) Remove the old `$export_file` or place it in a different directory 88 | 89 | From here you can now either set up the project as a git/subversion/etc. repository or install into apex using: 90 | ``` 91 | npm run new-conf-file 92 | npm run switch-conf-file #unnecessary if you choose to switch to your new config file in npm run new-conf-file 93 | npm run file-to-apex 94 | ``` 95 | ## Working with existing apex-source-control project 96 | 97 | 1) Clone the repository locally 98 | 99 | 2) Use `npm install` to install dependencies 100 | 101 | 3) Run the following commands and follow the prompts. This will set up your config file and put the application into your APEX workspace 102 | ``` 103 | npm run new-conf-file #create config file with the info of the app you want to use for version control 104 | npm run switch-conf-file #unnecessary if you choose to switch to your new config file in npm run new-conf-file 105 | npm run file-to-apex #download the app locally 106 | ``` 107 | That's it! 108 | 109 | When you run `npm run new-conf-file` you can either enter the info of the app you are already using to develop (your version will be overwritten by the one in version control) or enter in the info of a non-existent app which will be automatically created after running `npm run file-to-apex` (see Configuration). 110 | 111 | ## npm scripts Commands 112 | Note: You can change these commands to anything you like by editing the scripts value of `package.json`. 113 | For example if you replaced `"apex-to-file" : "apex-source-control apex-to-file"` with `"atf" : "apex-source-control apex-to-file"` then your new command would be `npm run atf`. 114 | ##### npm run apex-to-file 115 | Turn your apex workspace project into a file directory suitable for version control tools. 116 | ##### npm run file-to-apex 117 | Import your file directory into apex as an apex application. 118 | Will overwrite any existing app in the same workspace with the same id. 119 | 120 | This command will only work with a $PROJECT_HOME/apex/ dir which was generated by `npm run apex-to-file` or is setup as described in steps 6 to 8 of Setting up from an APEX export file. 121 | ##### npm run uninstall-apex 122 | Uninstall your app from apex 123 | ##### npm run new-conf-file 124 | Creates a new config file using user input. 125 | ##### npm run switch-conf-file 126 | Switches the symlink to the config file of user's choice 127 | ##### npm run read-conf-file 128 | Outputs the name and contents of the config file currently being used 129 | ##### npm run generate-app-id 130 | Logs into your database and automatically generates an unused app-id. 131 | Used in order to avoid accidentaly overwritting someone else's app. 132 | Can only be run if your config file is set up with proper database login info (see Config file examples). 133 | ###Workflow & Project Sanitation 134 | * Ignore config files in version control (.gitignore for git) since you'll likely not want to share login info in version control. 135 | If you want to leave the configs in version control then you should at least ignore the asc.conf symlink to avoid unnecessary noise 136 | * Any non apex files you wish to put into version control along with your apex app should be placed in a project-top-level dir called non-apex 137 | * After merging your code you should `npm run file-to-apex` and test that the merge didn't break anything 138 | If you have automated tests you can just run those instead 139 | * It is good practice to start your day with a merge 140 | 141 | ## Configuration 142 | Config files are placed in a top level config/ directory. 143 | Each developer has their own config file with their app data which is then refrenced by a symlink named `asc.conf` (short for apex-source-control.conf). 144 | Config files should not be put under version control (unless you want to share connection data for some reason). 145 | ##### apexappid 146 | The unique application id # of your APEX app in Oracle. 147 | Used to tell APEX which application to export. 148 | 149 | The application id does not have to already exist in APEX in order to be installed by `npm run file-to-apex` but must not conflict with an app id in a different workspace. 150 | It can be the same as a pre-existing APEX app in the same workspace, but be careful about overwriting work that isn't your own. 151 | ##### workspace_name 152 | Name of the workspace where the app is installed or is to be installed to. 153 | The workspace should already exist before usage 154 | ##### parsing_schema 155 | The parsing_schema used by APEX for this app. 156 | If set incorrectly the app may not function at all when imported into APEX. 157 | ##### app_alias 158 | The app alias to be used by APEX. Should usually not be set. 159 | 160 | Since aliases must be unique within a workspace (and recommended to be unique within an instance) this should only be set for important versions of the app (i.e. production or dev versions). 161 | If this field is left blank an app_alias will be auto-generated ('F' + $apexappid) to avoid conflicts. 162 | ##### database_connection 163 | The database_connection info in [JDBC](http://www.orafaq.com/wiki/JDBC) format, i.e. Hostname:Port/SID. Example: `myhost:1521/orcl`. 164 | Used in connect statements such as `sqlplus $username/$password@$database_connection` 165 | ##### username 166 | Username of database login 167 | ##### password 168 | Password of database login 169 | #### Config file examples 170 | A normal developer config file 171 | ``` 172 | apexappid=116 173 | workspace_name=TEST_WORKSPACE 174 | parsing_schema=PARSER 175 | app_alias= 176 | database_connection=localhost:1521/xe 177 | username=the_coolest_guy 178 | password=no_really_the_coolest 179 | ``` 180 | A config file for using `npm run generate-app-id` 181 | ``` 182 | apexappid= 183 | workspace_name= 184 | parsing_schema= 185 | app_alias= 186 | database_connection=localhost:1521/xe 187 | username=the_coolest_guy 188 | password=no_really_the_coolest 189 | ``` 190 | A config file for a production version of an app 191 | ``` 192 | apexappid=113 193 | workspace_name=PROD_APPS 194 | parsing_schema=PARSER 195 | app_alias=Hello_World 196 | database_connection=localhost:1521/xe 197 | username=i_wanna_be_the_very_best 198 | password=that_no_one_ever_was 199 | ``` 200 | ###Known Issues 201 | #### Deleting a page will always win merges 202 | **To Reproduce**: 203 | * Have two developers working on different versions of an app (i.e. working on different features to be added). 204 | * Developer 1 deletes a page in the app and pushes to the project's repo. 205 | * Developer 2 merges developer 1's changes and decides to keep the deleted page in the merge. 206 | * When developer 2 reinstalls the app into apex the page will still be deleted. 207 | 208 | **Why it occurs**: 209 | 210 | Since a page was deleted, it's removed from the install.sql script generated by apex. When git sees the merge of the two developers, only one has changed the install.sql script so it automatically applies those changes to the merge meaning the page is no longer called during install so it's never created in your apex application. 211 | 212 | **How to avoid**: 213 | 214 | Be extremely careful when deleting pages and consult with your teammates first 215 | 216 | **How to fix**: 217 | 218 | If you do end up accidently deleting a page then you will have to manually re-add the install lines to the install.sql 219 | 220 | #### Random version control noise in application meta-data 221 | On every commit there will be conflicts within the create_application.sql (and likely some others) which will have to be resolved by hand. 222 | Unfortunately there is no easy way to avoid this, but as you continue to use this tool you'll be able to quickly identify which files have real changes and which are garbage. 223 | 224 | ## Inspiration 225 | These scripts were designed using [this paper] (http://www.rwijk.nl/AboutOracle/psdua.pdf) as reference. 226 | There's a lot of good information about directory structure and general developer workflow so it's definitely worth a read. 227 | 228 | It should be noted that if you decide to read the paper we have changed the names/functions of the scripts slightly: file-to-apex=install_apex.sql; uninstall-apex=uninstall_apex.sql; and apex-to-file=apexupdate.sh (Except we don't auto-update using subversion in order to leave more user freedom) 229 | 230 | ## Developers 231 | To release: 232 | 233 | - update the version in package.json 234 | - tag with matching tag. E.g. `git tag -a v1.3.2` 235 | - deploy to npm repo `npm publish` 236 | 237 | -------------------------------------------------------------------------------- /apex-source-control: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function main { 4 | case "${1}" in 5 | 'test') 6 | ./scripts/check_conf_file.sh 7 | ;; 8 | 'apex-to-file') 9 | apex_to_file 10 | ;; 11 | 'file-to-apex') 12 | file_to_apex 13 | ;; 14 | 'new-conf-file') 15 | new_conf_file 16 | ;; 17 | 'switch-conf-file') 18 | switch_conf_file 19 | ;; 20 | 'read-conf-file') 21 | read_conf_file 22 | ;; 23 | 'generate-app-id') 24 | generate_app_id 25 | ;; 26 | 'uninstall-apex') 27 | uninstall_apex 28 | ;; 29 | get-connect-string|connect-string) 30 | get_connect_string 31 | ;; 32 | *) 33 | echo "apex-source-control: bad command" 34 | ;; 35 | esac 36 | } 37 | 38 | function die { 39 | echo $@ 40 | exit 1 41 | } 42 | 43 | function upcase { 44 | echo "$@" | tr '[a-z-]' '[A-Z_]' 45 | } 46 | 47 | function get_connect_string { 48 | source ./config/asc.conf 49 | local connect_string="${username}/${password}@${database_connection}" 50 | echo "${connect_string}" 51 | } 52 | 53 | 54 | function run_sql_cmd { 55 | local connect_string="$(get_connect_string)" 56 | local sql_cmd 57 | if which sql > /dev/null; then 58 | echo Running PWD $PWD 59 | sql_cmd="sql -S" 60 | else 61 | echo WARNING: could not find SQLcl \(sql\). Falling back to sqlplus 62 | sql_cmd="sqlplus -S" 63 | fi 64 | 65 | NLS_LANG=.AL32UTF8 66 | export NLS_LANG 67 | 68 | ${sql_cmd} ${connect_string} "$@" || die Failed: ${sql_cmd} ${connect_string} "$@" 69 | } 70 | 71 | function check_conf_file { 72 | if [ ! -e ./config/asc.conf ]; then 73 | echo "Missing or broken symbolic link: ${PWD}/config/asc.conf" 74 | echo "Please use the command 'npm run switch-conf-file' to create the symbolic link ./config/asc.conf to your config file" 75 | echo "If you don't have a config file set up you can create one using 'npm run new-config-file'" 76 | exit 1 77 | fi 78 | 79 | echo "Using the config file at: $(readlink config/asc.conf)" 80 | 81 | source ./config/asc.conf 82 | 83 | [ -z "${apexappid}" ] && die "Missing config: ${apexappid} apexappid" 84 | [ -z "${workspace_name}" ] && die "Missing config: ${workspace_name} workspace_name" 85 | [ -z "${database_connection}" ] && die "Missing config: ${database_connection} database_connection" 86 | [ -z "${username}" ] && die "Missing config: ${username} username" 87 | [ -z "${password}" ] && die "Missing config: ${password} password" 88 | 89 | echo "Config file looks good! Moving on to the next step..." 90 | } 91 | 92 | function legacy_apex_export { 93 | 94 | [ -z "${ORACLE_HOME}" ] && die "Missing environment variable: ORACLE_HOME. Please add the path of your oracle installation to your environment variables under the variable name ORACLE_HOME" 95 | [ -z "${APEX_HOME}" ] && die "Missing environment variable: APEX_HOME. Please add the path of your apex installation to your environment variables under the variable name APEX_HOME. Note: You should be using APEX version 5 or above" 96 | [ ! -e "${ORACLE_HOME}"/jdbc/lib/ojdbc6.jar ] && die "Missing ojdbc6.jar: please download from oracle and put in ${ORACLE_HOME}/jdbc/lib directory" 97 | [ ! -e "${APEX_HOME}"/utilities/oracle/apex/APEXExport.class ] && die "Missing APEXExport class. Please ensure you are using Apex 5 or above and have the APEXExport and APEXExportSplitter classes are in the $APEX_HOME/utilities/oracle/apex/ directory" 98 | [ ! -e "${APEX_HOME}"/utilities/oracle/apex/APEXExportSplitter.class ] && die "Missing APEXExportSplitter class. Please ensure you are using Apex 5 or above and have the APEXExport and APEXExportSplitter classes are in the $APEX_HOME/utilities/oracle/apex/ directory" 99 | 100 | source ./config/asc.conf 101 | 102 | export CLASSPATH="${APEX_HOME}"/utilities:"${ORACLE_HOME}"/jdbc/lib/ojdbc6.jar 103 | 104 | export_file="f${apexappid}.sql" 105 | 106 | if [ -d apex/ ]; then 107 | rm -r apex/ 108 | fi 109 | if [ -e "${export_file}" ]; then 110 | rm "${export_file}" 111 | fi 112 | 113 | java oracle.apex.APEXExport -db "${database_connection}" -user "${username}" -password "${password}" -applicationid "${apexappid}" -skipExportDate -expOriginalIds || 114 | die "Exit code #: $?. An error has occured while trying to use APEXExport. Please check that your database_connection, username, password, and apexappid variables are all set correctly." 115 | 116 | java oracle.apex.APEXExportSplitter "${export_file}" || 117 | die "Exit code #: $?. An error has occured while trying to use APEXExportSplitter. Please check that your apexappid variable is set correctly and that the application exists in the workspace you are trying to export from" 118 | 119 | rm "${export_file}" 120 | 121 | mv "f${apexappid}" apex # 122 | 123 | perl -pi -e 's^\@application^\@apex/application^' apex/install.sql 124 | } 125 | 126 | function apex_export { 127 | if which sql > /dev/null; then 128 | source ./config/asc.conf 129 | echo Using sqlcl to export app ${apexappid} 130 | export_file="f${apexappid}.sql" 131 | 132 | if [ -d apex/ ]; then 133 | rm -r apex/ 134 | fi 135 | if [ -e "${export_file}" ]; then 136 | rm "${export_file}" 137 | fi 138 | 139 | run_sql_cmd @/dev/stdin < p_workspace_name); 183 | APEX_APPLICATION_INSTALL.SET_WORKSPACE_ID ( l_workspace_id ); 184 | 185 | IF p_offset IS NOT NULL THEN 186 | apex_application_install.set_offset( p_offset ); 187 | ELSE 188 | apex_application_install.generate_offset; 189 | END IF; 190 | end; 191 | / 192 | @apex/install.sql 193 | / 194 | quit 195 | EOF 196 | 197 | } 198 | 199 | function generate_app_id { 200 | source config/asc.conf || die 201 | 202 | tmpfile=$(mktemp -t generate_app_id) 203 | 204 | run_sql_cmd @/dev/stdin < ${tmpfile} || exit 1 205 | set serveroutput on 206 | set feedback off 207 | begin 208 | apex_application_install.generate_application_id; 209 | dbms_output.put_Line(apex_application_install.get_application_id); 210 | end; 211 | / 212 | exit 213 | ENDSQL 214 | 215 | app_id=$(cat $tmpfile) 216 | 217 | rm $tmpfile 218 | 219 | if [ -h config/asc.conf ]; then 220 | conf_file=$(readlink ./config/asc.conf) 221 | echo -n "The id '${app_id}' will be written to '${conf_file}'. Is this alright? [y/N]: " 222 | read can_write 223 | 224 | if [ "${can_write}" == "y" ]; then 225 | perl -pi -e "s/^apexappid=.*/apexappid=${app_id}/" config/${conf_file} 226 | echo "apexappid in '${conf_file}' has been replaced with '${app_id}'. The new config file looks like this: " 227 | cat config/${conf_file} 228 | else 229 | echo -n "The generated app id was not written to file. " 230 | echo "You can change this manually by changing the apexappid of your config to '${app_id}'" 231 | fi 232 | fi 233 | 234 | } 235 | 236 | function new_conf_file { 237 | echo "Creating new config file..." 238 | 239 | echo -n "Please enter the file name of your config file: " 240 | read conf_file 241 | if [ -z "${conf_file}" ]; then 242 | echo "Please input a value for your config file name before pressing enter"; exit 1 243 | fi 244 | 245 | if [ -e ./config/"${conf_file}" ]; then 246 | echo "Sorry, the file ./scripts/${conf_file} already exists. Either delete the existing file or choose a different name and try again."; exit 1 247 | fi 248 | 249 | echo -n "Please enter the apexappid you would like to use. This should be chosen very carefully to avoid conflicts with other developers' app ids: " 250 | read apexappid 251 | 252 | echo -n "Please enter the name of your workspace: " 253 | read workspace_name 254 | 255 | echo -n "Please enter the parsing schema for the app you are using. Note that parsing schema should be all caps: " 256 | read parsing_schema 257 | 258 | echo "NOTE: The app_alias variable should only be set for well known versions of the app (i.e. production or some dev versions) in order to avoid potentially damaging conflicts. Press [ENTER] to leave the variable unset" 259 | echo -n "Please enter the app_alias for your app: " 260 | read app_alias 261 | 262 | echo -n "Please enter your Apex database connection in the following format [Hostname:port/SID]: " 263 | read database_connection 264 | 265 | echo -n "Please enter your username for the given database: " 266 | read username 267 | 268 | echo -n "Please enter your password: " 269 | read password 270 | 271 | if [ ! -d ./config/ ]; then 272 | mkdir config 273 | fi 274 | 275 | echo "apexappid=${apexappid}" > ./config/"${conf_file}" 276 | echo "workspace_name=${workspace_name}" >> ./config/"${conf_file}" 277 | echo "parsing_schema=${parsing_schema}" >> ./config/"${conf_file}" 278 | echo "app_alias=${app_alias}" >> ./config/"${conf_file}" 279 | echo "database_connection=${database_connection}" >> ./config/"${conf_file}" 280 | echo "username=${username}" >> ./config/"${conf_file}" 281 | echo "password=${password}" >> ./config/"${conf_file}" 282 | 283 | echo "Config file successfully generated! It looks like this:" 284 | 285 | cat ./config/"${conf_file}" 286 | 287 | echo "If anything looks wrong you can simply edit the file yourself at ./config/${conf_file}" 288 | 289 | echo 290 | 291 | echo -n "Would you like to switch to the new config file now [y/n]: " 292 | read switch_bool 293 | 294 | if [ "${switch_bool}" == "y" ]; then 295 | cd config 296 | if [ -h asc.conf ]; then 297 | rm asc.conf 298 | fi 299 | ln -s "${conf_file}" asc.conf 300 | cd .. 301 | echo "./config/asc.conf now points to ./config/$( readlink ./config/asc.conf )" 302 | else 303 | echo "Config file was not switched. Run 'npm run switch-conf-file' if you would like to change this." 304 | fi 305 | } 306 | 307 | function switch_conf_file { 308 | #!/bin/bash 309 | # 310 | # This script is designed to be called from the top level directory of your project and to be placed in the ./scripts/ directory 311 | 312 | echo "The availible config files are:" 313 | 314 | ls ./config/ | sed s/asc.conf// | sed -n '1!p' #print all conf files 315 | 316 | cd config 317 | 318 | if [ -z "${1}" ]; then 319 | echo -n "Please enter the config file you would like to use: " 320 | read conf_file 321 | else 322 | conf_file="${1}" 323 | fi 324 | 325 | if [ ! -e "${conf_file}" ]; then 326 | echo "Sorry, the file ./scripts/"${conf_file}" does not exist. Either create the file using npm run new-conf-file or choose a pre-existing config file."; exit 1 327 | fi 328 | 329 | if [ -h asc.conf ]; then 330 | rm asc.conf 331 | fi 332 | 333 | ln -s "${conf_file}" asc.conf 334 | 335 | cd .. 336 | 337 | echo "./config/asc.conf now points to ./config/$( readlink ./config/asc.conf )" 338 | 339 | echo "The new config file looks like this: " 340 | 341 | cat ./config/asc.conf 342 | } 343 | 344 | function unistall_apex { 345 | check_conf_file || die 346 | source config/asc.conf || exit 1 347 | 348 | run_sql_cmd @/dev/stdin "${apexappid}" "${workspace_name}" "${parsing_schema}" < p_workspace_name ); 357 | apex_application_install.set_schema( p_parsing_schema ); 358 | APEX_APPLICATION_INSTALL.SET_WORKSPACE_ID ( l_workspace_id ); 359 | end; 360 | / 361 | @apex/application/init.sql 362 | @apex/application/set_environment.sql 363 | @apex/application/delete_application.sql 364 | @apex/application/end_environment.sql 365 | / 366 | quit 367 | 368 | EOF 369 | } 370 | 371 | # WIPMB is this useful? Can we delete it? 372 | function read_conf_file { 373 | 374 | if [ ! -e ./config/asc.conf ]; then 375 | echo "Missing or broken symbolic link: ${PWD}/config/asc.conf" 376 | echo "Please use the command 'npm run switch-conf-file' to create the symbolic link ./config/asc.conf to your config file" 377 | echo "If you don't have a config file set up you can create one using 'npm run new-config-file'" 378 | exit 1 379 | fi 380 | 381 | echo "The config file currently being used is: " 382 | readlink ./config/asc.conf 383 | 384 | echo 385 | 386 | echo "The config file looks like this: " 387 | cat ./config/asc.conf 388 | 389 | echo "If any of the data looks wrong you can simply edit the file yourself at ./config/$(readlink ./config/asc.conf)" 390 | } 391 | 392 | 393 | main "$@" 394 | --------------------------------------------------------------------------------